Commit 1af7ee74 by Hasnain

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

parent 8c205ad8
"""
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)
"""
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()
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