Commit c7a60973 by Clinton Blackburn

Integrated edxapp

The LMS and Studio (CMS) are now part of devstack!

ECOM-6560
parent d0dadc4e
......@@ -18,15 +18,12 @@ devstack.provision: ## Provision all services
devstack.reset: ## Remove all service containers
docker-compose down
devstack.start: loopback ## Start all services
devstack.start: ## Start all services
docker-compose up
devstack.stop: ## Stop all services
docker-compose stop
loopback: ## Create loopback alias
./loopback.sh
requirements: ## Install requirements
pip install -r requirements.txt
......
......@@ -6,7 +6,9 @@ This project is meant to replace the traditional Vagrant VM "devstack" with a mu
## Getting Started
All of the services can be run by following the steps below.
All of the services can be run by following the steps below. Note that since we are running many containers, you should
configure Docker with a sufficient amount of resources. Our testing found that [configuring Docker for Mac](https://docs.docker.com/docker-for-mac/#/advanced)
with 2 CPUs and 4GB of memory works well.
1. The Docker Compose file mounts a host volume for each service's executing code. The host directory is expected to be
a sibling of this directory. For example, if this repo is cloned to `~/workspace/devstack`, host volumes will be
......@@ -38,28 +40,3 @@ After the services have started, if you need shell access to one of the services
```
$ make devstack.open.discovery
```
## Loopback Alias
Containers making requests to the LMS and Studio must communicate with ports exposed on the host system by the Vagrant VM. (This assumes that the LMS and Studio are still running in the traditional Vagrant-based devstack.)
This is fine on Linux, but doesn't work out of the box on OS X. Attempting to access localhost on a container will result
in talking to the Docker for Mac HyperKit VM, not the host machine.
While it's true that you can get this to work by accessing your Mac's external IP from your containers, this isn't ideal because
it won't work if you have no network access on your host. Your external IP also changes as you switch networks, meaning you'd have
to change the IP accessed by your containers every time you changed networks.
A better solution, borrowed from the [Docker forums](https://forums.docker.com/t/access-host-not-vm-from-inside-container/11747/10),
is to give your host a fixed address by creating a [loopback](http://askubuntu.com/questions/247625/what-is-the-loopback-device-and-how-do-i-use-it)
alias. This is done for you by the `devstack.start` target.
The result is a fixed IP which your containers can use to access ports on your host machine. Note that the underlying script uses `sudo`;
adding IP addresses requires root access. Also note that the alias will not survive a host reboot, which is why the `devstack.start` target
always attempts to set up the loopback for you.
Part of the loopback alias setup includes adding a line to the `/etc/hosts` file on your machine. If you want to stop using devstack, you can clean this up by opening your `/etc/hosts` file and removing this line:
```
10.254.254.254 docker.host
```
......@@ -29,6 +29,14 @@ services:
# ports:
# - "11211:11211"
mongo:
container_name: edx.devstack.mongo
image: mongo:2.6.5
# ports:
# - "27017:27017"
volumes:
- mongo_data:/data/db
mysql:
command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
container_name: edx.devstack.mysql
......@@ -67,9 +75,6 @@ services:
environment:
TEST_ELASTICSEARCH_URL: "http://edx.devstack.elasticsearch:9200"
ENABLE_DJANGO_TOOLBAR: 1
extra_hosts:
# For server-to-server calls.
- "docker.host:10.254.254.254"
image: edxops/discovery:devstack
ports:
- "18381:18381"
......@@ -91,6 +96,20 @@ services:
volumes:
- ../ecommerce:/edx/app/ecommerce/ecommerce
edxapp:
command: /edx/app/edxapp/devstack.sh start
container_name: edx.devstack.edxapp
depends_on:
- mysql
- memcached
- mongo
image: edxops/edxapp:devstack
ports:
- "18000:18000"
- "18010:18010"
volumes:
- ../edx-platform:/edx/app/edxapp/edx-platform
programs:
command: /edx/app/programs/devstack.sh start
container_name: edx.devstack.programs
......@@ -107,4 +126,5 @@ services:
volumes:
elasticsearch_data:
mongo_data:
mysql_data:
#!/usr/bin/env bash
# Dump the specified database to a file of the same name.
#
# Example:
# $ dump-db edxapp
#
# This will dump the edxapp database to a file named exapp.sql.
if [ -z "$1" ]
then
echo "You must supply a database name!"
exit 1
fi
echo "Dumping the $1 database..."
docker exec -i edx.devstack.mysql mysqldump --skip-add-drop-table -B $1 > $1.sql
echo "Finished dumping the $1 database!"
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
#!/usr/bin/env bash
# Load the specified database from a file of the same name.
#
# Example:
# $ load-db edxapp
#
# This will load the edxapp database from a file named exapp.sql.
if [ -z "$1" ]
then
echo "You must supply a database name!"
exit 1
fi
echo "Loading the $1 database..."
docker exec -i edx.devstack.mysql mysql -uroot $1 < $1.sql
echo "Finished loading the $1 database!"
# It's not necessary to do this on Linux, so check that we're on OS X before continuing.
if [ "$(uname)" == "Darwin" ]; then
# This loopback alias allows access to OS X hosts from inside a container. 10.254.254.254
# is a private IP likely to be unused. As is, the alias will not survive a reboot.
# Borrowed from https://forums.docker.com/t/access-host-not-vm-from-inside-container/11747/10.
sudo ifconfig lo0 alias 10.254.254.254
# This will check for a "docker.host" entry in your hosts file. It's created if it doesn't
# exist, so you can use URLs like "http://docker.host:8000" in your service configuration.
if ! grep -q "docker.host" /etc/hosts; then
# Using tee to write to /etc/hosts because using >> to append isn't allowed, even as root.
# See http://stackoverflow.com/a/550808.
echo "10.254.254.254 docker.host" | sudo tee -a /etc/hosts > /dev/null
fi
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
if ! grep -q "docker.host" /etc/hosts; then
echo "127.0.0.1 docker.host" | sudo tee -a /etc/hosts > /dev/null
fi
fi
conn = new Mongo();
users = [
{
'user': 'admin',
'pwd': 'password',
'roles': ['root'],
'database': 'admin'
},
{
'user': 'cs_comments_service',
'pwd': 'password',
'roles': ['readWrite'],
'database': 'cs_comments_service'
},
{
'user': 'edxapp',
'pwd': 'password',
'roles': ['readWrite'],
'database': 'edxapp'
}
];
for (var i = 0; i < users.length; i++) {
var user = users[i];
var username = user.user;
var db = conn.getDB(user.database);
delete user.database;
if (db.getUser(username) == null) {
db.createUser(user);
} else {
delete user.user;
db.updateUser(username, user);
}
}
#!/usr/bin/env bash
# This script will provision all of the services. Each service will be setup in the following manner:
#
# 1. Migrations run,
# 2. Tenants—as in multi-tenancy—setup,
# 3. Service users and OAuth clients setup in LMS,
# 4. Static assets compiled/collected.
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# Bring the database online.
docker-compose up -d mysql
# Bring the databases online.
docker-compose up -d mysql mongo
n=0
db_limit=5
db_sleep_time=2
until [ $n -ge $db_limit ]
# Ensure the MySQL server is online and usable
echo "Waiting for MySQL"
until docker exec -i edx.devstack.mysql mysql -uroot -se "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'root')" &> /dev/null
do
docker exec -i edx.devstack.mysql mysql -uroot < provision.sql && echo -e "${GREEN}Service databases and users have been created.${NC}" && break
n=$[$n+1]
echo -e "${YELLOW}Waiting ${db_sleep_time} seconds for MySQL to come online...${NC}"
sleep 2
printf "."
sleep 1
done
if [ $n -ge $db_limit ]
then
echo -e "${RED}Failed to create service databases and users!${NC}"
exit 1
fi
# In the event of a fresh MySQL container, wait a few seconds for the server to restart
# This can be removed once https://github.com/docker-library/mysql/issues/245 is resolved.
sleep 20
echo -e "MySQL ready"
echo -e "${GREEN}Creating databases and users...${NC}"
docker exec -i edx.devstack.mysql mysql -uroot mysql < provision.sql
docker exec -i edx.devstack.mongo mongo < mongo-provision.js
# Bring the rest of the services online
docker-compose up -d
# Run migrations, and create superusers that can access the services without the need for single sign-on.
services=('credentials' 'discovery' 'ecommerce' 'programs')
# Load database dumps for the largest databases to save time
./load-db.sh ecommerce
./load-db.sh edxapp
./load-db.sh edxapp_csmh
# Run edxapp migrations first since they are needed for the service users and OAuth clients
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && cd /edx/app/edxapp/edx-platform && paver update_db --settings devstack'
# Create a superuser for edxapp
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && python /edx/app/edxapp/edx-platform/manage.py lms --settings=devstack manage_user edx edx@example.com --superuser --staff'
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && echo "from django.contrib.auth import get_user_model; User = get_user_model(); user = User.objects.get(username=\"edx\"); user.set_password(\"edx\"); user.save()" | python /edx/app/edxapp/edx-platform/manage.py lms shell --settings=devstack' &
# We must fake an associative array for Bash 3 users
services=('credentials:18150' 'discovery:18381' 'ecommerce:18130' 'programs:18140')
for service in "${services[@]}"
do
echo -e "${GREEN}Running migrations for ${service}...${NC}"
docker exec -t edx.devstack.${service} bash -c 'source /edx/app/$1/$1_env && cd /edx/app/$1/$1/ && make migrate' -- "$service"
echo -e "${GREEN}Creating super-user for ${service}...${NC}"
docker exec -t edx.devstack.${service} bash -c 'source /edx/app/$1/$1_env && echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser(\"edx\", \"edx@example.com\", \"edx\") if not User.objects.filter(username=\"edx\").exists() else None" | python /edx/app/$1/$1/manage.py shell' -- "$service"
name=${service%%:*}
port=${service#*:}
echo -e "${GREEN}Running migrations for ${name}...${NC}"
docker exec -t edx.devstack.${name} bash -c 'source /edx/app/$1/$1_env && cd /edx/app/$1/$1/ && make migrate' -- "$name"
echo -e "${GREEN}Creating super-user for ${name}...${NC}"
docker exec -t edx.devstack.${name} bash -c 'source /edx/app/$1/$1_env && echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser(\"edx\", \"edx@example.com\", \"edx\") if not User.objects.filter(username=\"edx\").exists() else None" | python /edx/app/$1/$1/manage.py shell' -- "$name" &
echo -e "${GREEN}Creating service user and OAuth client for ${name}...${NC}"
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && python /edx/app/edxapp/edx-platform/manage.py lms --settings=devstack manage_user $1_worker $1_worker@example.com --staff' -- "$name" &
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && python /edx/app/edxapp/edx-platform/manage.py lms --settings=devstack create_oauth2_client "http://localhost:$2" "http://localhost:$2/complete/edx-oidc/" confidential --client_name $1 --client_id "$1-key" --client_secret "$1-secret" --trusted --logout_uri "http://localhost:$2/logout/" --username $1_worker' -- "$name" "$port" &
done
# TODO Create ecommerce tenant (ECOM-6564)
# Use create_or_update_site (https://github.com/edx/ecommerce/blob/master/ecommerce/core/management/commands/create_or_update_site.py)
docker exec -t edx.devstack.ecommerce bash -c 'source /edx/app/ecommerce/ecommerce_env && python /edx/app/ecommerce/ecommerce/manage.py oscar_populate_countries'
docker exec -t edx.devstack.ecommerce bash -c 'source /edx/app/ecommerce/ecommerce_env && python /edx/app/ecommerce/ecommerce/manage.py oscar_populate_countries' &
# TODO Create discovery tenant with correct credentials (ECOM-6565)
docker exec -t edx.devstack.discovery bash -c 'source /edx/app/discovery/discovery_env && python /edx/app/discovery/discovery/manage.py create_or_update_partner --code edx --name edX --courses-api-url "http://edx.devstack.edxapp:18000/api/courses/v1/" --ecommerce-api-url "http://edx.devstack.ecommerce:18130/api/v2/" --organizations-api-url "http://edx.devstack.edxapp:18000/api/organizations/v0/" --programs-api-url "http://edx.devstack.programs:18140/api/v1/" --oidc-url-root "http://edx.devstack.edxapp:18000/oauth2" --oidc-key discovery-key --oidc-secret discovery-secret' &
# TODO Create credentials tenant (ECOM-6566)
# Compile static assets. We do this last since it takes the longest.
services=('credentials' 'discovery' 'ecommerce' 'programs')
# Compile static assets last since they are absolutely necessary for all services. This will allow developers to get
# started if they do not care about static assets
for service in "${services[@]}"
do
echo -e "${GREEN}Compiling static assets for ${service}...${NC}"
docker exec -t edx.devstack.${service} bash -c 'source /edx/app/$1/$1_env && cd /edx/app/$1/$1/ && make static' -- "$service"
name=${service%%:*}
echo -e "${GREEN}Compiling static assets for ${name}...${NC}"
docker exec -t edx.devstack.${name} bash -c 'source /edx/app/$1/$1_env && cd /edx/app/$1/$1/ && make static' -- "$name" &
done
# TODO Consider loading demo course/users via Ansible play?
# Save the longest for last...
docker exec -t edx.devstack.edxapp bash -c 'source /edx/app/edxapp/edxapp_env && cd /edx/app/edxapp/edx-platform && paver update_assets --settings devstack'
echo -e "${GREEN}Provisioning complete!${NC}"
......@@ -7,5 +7,12 @@ GRANT ALL ON discovery.* TO 'discov001'@'%' IDENTIFIED BY 'password';
CREATE DATABASE IF NOT EXISTS ecommerce;
GRANT ALL ON ecommerce.* TO 'ecomm001'@'%' IDENTIFIED BY 'password';
CREATE DATABASE IF NOT EXISTS edxapp;
CREATE DATABASE IF NOT EXISTS edxapp_csmh;
GRANT ALL ON edxapp.* TO 'edxapp001'@'%' IDENTIFIED BY 'password';
GRANT ALL ON edxapp_csmh.* TO 'edxapp001'@'%';
CREATE DATABASE IF NOT EXISTS programs;
GRANT ALL ON programs.* TO 'programs001'@'%' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment