WL-1282 | Created management command for automation of theme setup.

This commit is contained in:
Hasnain
2017-10-13 17:58:01 +05:00
parent 8c205ad84c
commit 1af7ee7433
3 changed files with 331 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
"""
This command will be run by an ansible script.
"""
import os
import json
import fnmatch
import logging
from provider.oauth2.models import Client
from provider.constants import CONFIDENTIAL
from edx_oauth2_provider.models import TrustedClient
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
from openedx.core.djangoapps.theming.models import SiteTheme
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
LOG = logging.getLogger(__name__)
class Command(BaseCommand):
"""
Command to create the site, site themes, configuration and oauth2 clients for all WL-sites.
Example:
./manage.py lms create_sites_and_configurations --dns-name whitelabel --theme-path /edx/src/edx-themes/edx-platform
"""
dns_name = None
theme_path = None
ecommerce_user = None
discovery_user = None
def add_arguments(self, parser):
"""
Add arguments to the command parser.
"""
parser.add_argument(
"--dns-name",
type=str,
help="Enter DNS name of sandbox.",
required=True
)
parser.add_argument(
"--theme-path",
type=str,
help="Enter theme directory path",
required=True
)
def _create_oauth2_client(self, url, site_name, is_discovery=True):
"""
Creates the oauth2 client and add it in trusted clients.
"""
client, _ = Client.objects.get_or_create(
redirect_uri="{url}complete/edx-oidc/".format(url=url),
defaults={
"user": self.discovery_user if is_discovery else self.ecommerce_user,
"name": "{site_name}_{client_type}_client".format(
site_name=site_name,
client_type="discovery" if is_discovery else "ecommerce",
),
"url": url,
"client_id": "{client_type}-key-{site_name}".format(
client_type="discovery" if is_discovery else "ecommerce",
site_name=site_name
),
"client_secret": "{client_type}-secret-{dns_name}".format(
client_type="discovery" if is_discovery else "ecommerce",
dns_name=self.dns_name
),
"client_type": CONFIDENTIAL,
"logout_uri": "{url}logout/".format(url=url)
}
)
LOG.info("Adding {client} oauth2 client as trusted client".format(client=client.name))
TrustedClient.objects.get_or_create(client=client)
def _create_sites(self, site_domain, theme_dir_name, site_configuration):
"""
Create Sites, SiteThemes and SiteConfigurations
"""
site, created = Site.objects.get_or_create(
domain=site_domain,
defaults={"name": theme_dir_name}
)
if created:
LOG.info("Creating '{site_name}' SiteTheme".format(site_name=site_domain))
SiteTheme.objects.create(site=site, theme_dir_name=theme_dir_name)
LOG.info("Creating '{site_name}' SiteConfiguration".format(site_name=site_domain))
SiteConfiguration.objects.create(site=site, values=site_configuration, enabled=True)
else:
LOG.info("'{site_domain}' site already exists".format(site_domain=site_domain))
def find(self, pattern, path):
"""
Matched the given pattern in given path and returns the list of matching files
"""
result = []
for root, dirs, files in os.walk(path): # pylint: disable=unused-variable
for name in files:
if fnmatch.fnmatch(name, pattern):
result.append(os.path.join(root, name))
return result
def _get_sites_data(self):
"""
Reads the json files from theme directory and returns the site data in JSON format.
"site_a":{
"theme_dir_name": "site_a.edu.au"
"configuration": {
"key1": "value1",
"key2": "value2"
}
}
"""
site_data = {}
for config_file in self.find('sandbox_configuration.json', self.theme_path):
LOG.info("Reading file from {file}".format(file=config_file))
configuration_data = json.loads(
json.dumps(
json.load(
open(config_file)
)
).replace("{dns_name}", self.dns_name)
)['lms_configuration']
site_data[configuration_data['sandbox_name']] = {
"site_domain": configuration_data['site_domain'],
"theme_dir_name": configuration_data['theme_dir_name'],
"configuration": configuration_data['configuration']
}
return site_data
def get_or_create_service_user(self, username):
"""
Creates the service user for ecommerce and discovery.
"""
return User.objects.get_or_create(
username=username,
defaults={
"is_staff": True,
"is_superuser": True
}
)
def handle(self, *args, **options):
self.theme_path = options['theme_path']
self.dns_name = options['dns_name']
self.discovery_user, _ = self.get_or_create_service_user("lms_catalog_service_user")
self.ecommerce_user, _ = self.get_or_create_service_user("ecommerce_worker")
all_sites = self._get_sites_data()
# creating Sites, SiteThemes, SiteConfigurations and oauth2 clients
for site_name, site_data in all_sites.items():
site_domain = site_data['site_domain']
discovery_url = "https://discovery-{site_domain}/".format(site_domain=site_domain)
ecommerce_url = "https://ecommerce-{site_domain}/".format(site_domain=site_domain)
LOG.info("Creating '{site_name}' Site".format(site_name=site_name))
self._create_sites(site_domain, site_data['theme_dir_name'], site_data['configuration'])
LOG.info("Creating discovery oauth2 client for '{site_name}' site".format(site_name=site_name))
self._create_oauth2_client(discovery_url, site_name, is_discovery=True)
LOG.info("Creating ecommerce oauth2 client for '{site_name}' site".format(site_name=site_name))
self._create_oauth2_client(ecommerce_url, site_name, is_discovery=False)

View File

@@ -0,0 +1,156 @@
"""
Test cases for create_sites_and_configurations command.
"""
import mock
from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.management import call_command, CommandError
from provider.oauth2.models import Client
from edx_oauth2_provider.models import TrustedClient
from openedx.core.djangoapps.theming.models import SiteTheme
SITES = ['site_a', 'site_b']
def _generate_site_config(dns_name, site_domain):
""" Generate the site configuration for a given site """
return {
"lms_url": "{domain}-{dns_name}.sandbox.edx.org".format(domain=site_domain, dns_name=dns_name),
"platform_name": "{domain}-{dns_name}".format(domain=site_domain, dns_name=dns_name)
}
def _get_sites(dns_name):
""" Creates the mocked data for management command """
sites = {}
for site in SITES:
sites.update({
site: {
"theme_dir_name": "{}_dir_name".format(site),
"configuration": _generate_site_config(dns_name, site),
"site_domain": "{site}-{dns_name}.sandbox.edx.org".format(site=site, dns_name=dns_name)
}
})
return sites
class TestCreateSiteAndConfiguration(TestCase):
""" Test the create_site_and_configuration command """
def setUp(self):
super(TestCreateSiteAndConfiguration, self).setUp()
self.dns_name = "dummy_dns"
self.theme_path = "/dummyA/dummyB/"
def _assert_sites_are_valid(self):
"""
Checks that data of all sites is valid
"""
sites = Site.objects.all()
# there is an extra default site.
self.assertEqual(len(sites), len(SITES) + 1)
for site in sites:
if site.name in SITES:
site_theme = SiteTheme.objects.get(site=site)
self.assertEqual(
site_theme.theme_dir_name,
"{}_dir_name".format(site.name)
)
self.assertDictEqual(
dict(site.configuration.values),
_generate_site_config(self.dns_name, site.name)
)
def _assert_ecommerce_clients_are_valid(self):
"""
Checks that all ecommerce clients are valid
"""
service_user = User.objects.filter(username="ecommerce_worker")
self.assertEqual(len(service_user), 1)
self.assertTrue(service_user[0].is_staff)
clients = Client.objects.filter(user=service_user)
self.assertEqual(len(clients), len(SITES))
for client in clients:
self.assertEqual(client.user.username, service_user[0].username)
site_name = client.name[:6]
ecommerce_url = "https://ecommerce-{site_name}-{dns_name}.sandbox.edx.org/".format(
site_name=site_name,
dns_name=self.dns_name
)
self.assertEqual(client.url, ecommerce_url)
self.assertEqual(
client.redirect_uri,
"{ecommerce_url}complete/edx-oidc/".format(ecommerce_url=ecommerce_url)
)
self.assertEqual(
len(TrustedClient.objects.filter(client=client)),
1
)
def _assert_discovery_clients_are_valid(self):
"""
Checks that all discovery clients are valid
"""
service_user = User.objects.filter(username="lms_catalog_service_user")
self.assertEqual(len(service_user), 1)
self.assertTrue(service_user[0].is_staff)
clients = Client.objects.filter(user=service_user)
self.assertEqual(len(clients), len(SITES))
for client in clients:
self.assertEqual(client.user.username, service_user[0].username)
site_name = client.name[:6]
discovery_url = "https://discovery-{site_name}-{dns_name}.sandbox.edx.org/".format(
site_name=site_name,
dns_name=self.dns_name
)
self.assertEqual(client.url, discovery_url)
self.assertEqual(
client.redirect_uri,
"{discovery_url}complete/edx-oidc/".format(discovery_url=discovery_url)
)
self.assertEqual(
len(TrustedClient.objects.filter(client=client)),
1
)
def test_without_dns(self):
""" Test the command without dns_name """
with self.assertRaises(CommandError):
call_command(
"create_sites_and_configurations"
)
@mock.patch(
'openedx.core.djangoapps.theming.management.commands.create_sites_and_configurations.Command._get_sites_data'
)
def test_with_dns(self, mock_get_sites):
""" Test the command with dns_name """
mock_get_sites.return_value = _get_sites(self.dns_name)
call_command(
"create_sites_and_configurations",
"--dns-name", self.dns_name,
"--theme-path", self.theme_path
)
self._assert_sites_are_valid()
self._assert_discovery_clients_are_valid()
self._assert_ecommerce_clients_are_valid()
call_command(
"create_sites_and_configurations",
"--dns-name", self.dns_name,
"--theme-path", self.theme_path
)
# if we run command with same dns then it will not duplicates the sites and oauth2 clients.
self._assert_sites_are_valid()
self._assert_discovery_clients_are_valid()
self._assert_ecommerce_clients_are_valid()