def test_set_appserver_active(self, mocks, mock_enable_monitoring):
     """
     Check that monitoring is enabled when an appserver is activated.
     """
     instance = OpenEdXInstanceFactory()
     appserver_id = instance.spawn_appserver()
     appserver = instance.appserver_set.get(pk=appserver_id)
     appserver.make_active()
     self.assertEqual(mock_enable_monitoring.call_count, 1)
示例#2
0
 def test_one_attempt_default(self):
     """
     Test that by default, the spawn_appserver task will not re-try provisioning.
     """
     instance = OpenEdXInstanceFactory()
     self.mock_spawn_appserver.return_value = None  # Mock provisioning failure
     tasks.spawn_appserver(instance.ref.pk)
     self.assertEqual(self.mock_spawn_appserver.call_count, 1)
     self.assertTrue(any("Spawning new AppServer, attempt 1 of 1" in log.text for log in instance.log_entries))
 def test_ansible_settings_no_mongo_server(self):
     """
     Don't add mongo ansible vars if instance has no MongoDB server
     """
     self.instance = OpenEdXInstanceFactory()
     self.instance.mongodb_server = None
     self.instance.save()
     appserver = make_test_appserver(self.instance)
     self.check_mongo_vars_not_set(appserver)
示例#4
0
 def test_get_s3_connection(self, s3_region, expected_hostname):
     """
     Test get_s3 connection returns right instance
     """
     instance = OpenEdXInstanceFactory()
     instance.s3_region = s3_region
     s3_connection = instance.get_s3_connection()
     self.assertIsInstance(s3_connection, boto.s3.connection.S3Connection)
     self.assertEqual(s3_connection.host, expected_hostname)
 def test_ignore_errors_deprovision_mongo(self, mock_mongo_client_cls,
                                          *mock_methods):
     """
     Test mongo is set as deprovision when ignoring errors.
     """
     self.instance = OpenEdXInstanceFactory()
     self.instance.mongo_provisioned = True
     self.instance.deprovision_mongo(ignore_errors=True)
     self.assertFalse(self.instance.mongo_provisioned)
 def test_secret_key_settings_no_key(self, mock_consul):
     """
     Test that secret key settings are empty if the master key is not set.
     """
     instance = OpenEdXInstanceFactory()
     make_test_appserver(instance)
     instance.secret_key_b64encoded = ''
     instance.save()
     self.assertEqual(instance.get_secret_key_settings(), '')
 def test_youtube_api_key_unset(self):
     """
     Check that EDXAPP_YOUTUBE_API_KEY is set to None by default.
     """
     instance = OpenEdXInstanceFactory(sub_domain='youtube.apikey',
                                       use_ephemeral_databases=True)
     appserver = make_test_appserver(instance)
     configuration_vars = yaml.load(appserver.configuration_settings)
     self.assertIsNone(configuration_vars['EDXAPP_YOUTUBE_API_KEY'])
示例#8
0
    def test_admin_users(self, mock_consul):
        """
        By default, all users that belong to an organization that owns the
        server and OCIM admins have access to the sandbox.
        """
        admin_org_handle = 'admin-org'
        OrganizationFactory(name=admin_org_handle, github_handle=admin_org_handle)

        users = [
            ('user', 'user', admin_org_handle),
            ('admin2', 'admin2', admin_org_handle),
            ('no_github_handle', '', admin_org_handle),
            ('inactive_user', 'inactive_user', admin_org_handle),
            ('another_org', 'another_org', 'another_org'),
            ('no_org_1', 'no_org_1', ''),
            ('no_org_2', 'no_org_2', None),
        ]
        admin_users = [
            ('admin1', 'admin1', admin_org_handle),
            ('admin3', 'admin3', 'another_org'),
            ('admin4', 'admin4', ''),
            ('admin5', 'admin5', None),
            ('admin_no_org', '', admin_org_handle),
            ('admin_no_github', 'invalid_github_user', admin_org_handle),
            ('inactive_admin', 'inactive_admin', admin_org_handle),
        ]

        expected_admin_users = ['user', 'admin1', 'admin2', 'admin3', 'admin4', 'admin5']

        for username, github_handle, org_handle in users:
            make_user_and_organization(username, github_username=github_handle, org_handle=org_handle)

        for username, github_handle, org_handle in admin_users:
            profile, _ = make_user_and_organization(username, github_username=github_handle, org_handle=org_handle)
            profile.user.is_superuser = True
            profile.user.save()

        # Mark the inactive user and admin as inactive; they should not be added to the resulting list
        get_user_model().objects.filter(username__in=('inactive_user', 'inactive_admin')).update(is_active=False)

        def check(_users):
            return [_user for _user in _users if _user != 'invalid_github_user']

        with patch('instance.models.mixins.openedx_config.check_github_users', check):
            appserver = make_test_appserver(
                OpenEdXInstanceFactory(),
                organization=Organization.objects.get(github_handle=admin_org_handle),
            )

            # Check user with non existant Github hande is removed
            self.assertEqual(len(appserver.admin_users) - 1, len(expected_admin_users))

            ansible_settings = yaml.load(appserver.configuration_settings, Loader=yaml.SafeLoader)
            self.assertCountEqual(ansible_settings['COMMON_USER_INFO'], [
                {'name': name, 'github': True, 'type': 'admin'} for name in expected_admin_users
            ])
 def test_spawn_appserver_no_organization(self):
     """
     POST - An instance manager user without an organization can't spawn servers
     (because they can't see any instance either).
     """
     self.api_client.login(username='******', password='******')
     instance = OpenEdXInstanceFactory()
     response = self.api_client.post('/api/v1/openedx_appserver/',
                                     {'instance_id': instance.ref.pk})
     self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
示例#10
0
 def test_get_details(self):
     """
     GET - Detailed attributes
     """
     self.api_client.login(username='******', password='******')
     instance = OpenEdXInstanceFactory(sub_domain='domain.api')
     response = self.api_client.get(
         '/api/v1/instance/{pk}/'.format(pk=instance.ref.pk))
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     self.check_serialized_instance(response.data, instance)
示例#11
0
 def test_deprovision_swift(self, create_swift_container, mock_consul):
     """
     Test deprovisioning Swift containers.
     """
     instance = OpenEdXInstanceFactory()
     instance.storage_type = StorageContainer.SWIFT_STORAGE
     instance.provision_swift()
     self.check_swift(instance, create_swift_container)
     instance.deprovision_swift()
     self.assertIs(instance.swift_provisioned, False)
 def test_provision_mysql_weird_domain(self, mock_consul):
     """
     Make sure that database names are escaped correctly
     """
     sub_domain = 'really.really.really.really.long.subdomain'
     base_domain = 'this-is-a-really-unusual-domain-แปลกมาก.com'
     internal_lms_domain = '{}.{}'.format(sub_domain, base_domain)
     self.instance = OpenEdXInstanceFactory(internal_lms_domain=internal_lms_domain)
     self.instance.provision_mysql()
     self.check_mysql()
 def test_ignore_errors_deprovision_mysql(self, mock_drop_user,
                                          mock_drop_database,
                                          mock_get_cursor):
     """
     Test mysql is set as deprovision when ignoring errors.
     """
     self.instance = OpenEdXInstanceFactory()
     self.instance.mysql_provisioned = True
     self.instance.deprovision_mysql(ignore_errors=True)
     self.assertFalse(self.instance.mysql_provisioned)
 def test_deprovision_mongo(self, mock_mongo_client_cls,
                            mock_get_main_db_url):
     """
     Test deprovision_mongo calls drop_database.
     """
     self.instance = OpenEdXInstanceFactory()
     self.instance.mongo_provisioned = True
     self.instance.deprovision_mongo()
     for database in self.instance.mongo_database_names:
         mock_mongo_client_cls().drop_database.assert_any_call(database)
示例#15
0
 def test_default_component_versions(self, component_version):
     """
     Test the default value of components' version
     """
     instance = OpenEdXInstanceFactory(name='Vars Instance',
                                       email='*****@*****.**',
                                       openedx_release='dummy-release')
     appserver = make_test_appserver(instance)
     self.assertIn('{}: dummy-release'.format(component_version),
                   appserver.configuration_settings)
 def test_ansible_settings_mongo(self, mock_consul):
     """
     Add mongo ansible vars if instance has a MongoDB server
     """
     # Delete MongoDBServer object created during the migrations to allow the settings override
     # to take effect.
     MongoDBServer.objects.all().delete()
     self.instance = OpenEdXInstanceFactory()
     appserver = make_test_appserver(self.instance)
     self.check_mongo_vars_set(appserver, expected_hosts=['mongo.opencraft.com'])
示例#17
0
 def add_active_appserver(self, sub_domain='domain.api'):
     """
     Create an instance, and add an active appserver.
     """
     self.api_client.login(username='******', password='******')
     instance = OpenEdXInstanceFactory(sub_domain=sub_domain)
     app_server = make_test_appserver(instance)
     app_server.is_active = True  # Outside of tests, use app_server.make_active() instead
     app_server.save()
     return instance, app_server
    def test_long_monitoring_condition_names(self, additional_emails, expected_emails, mock_newrelic, mock_consul):
        """
        Check that the `enable_monitoring` method creates New Relic Synthetics
        monitors for each of the instance's public urls, and enables email
        alerts even for those instance names which has extremely long names.
        """
        monitor_ids = [str(uuid4()) for i in range(4)]
        mock_newrelic.get_synthetics_monitor.return_value = []
        mock_newrelic.get_synthetics_notification_emails.return_value = []
        mock_newrelic.create_synthetics_monitor.side_effect = monitor_ids
        mock_newrelic.add_alert_policy.return_value = 1
        mock_newrelic.add_alert_nrql_condition.side_effect = list(range(4))
        mock_newrelic.add_email_notification_channel.side_effect = list(range(len(expected_emails)))
        instance = OpenEdXInstanceFactory()
        instance.name += " long" * 15  # Generate a very long instance name
        instance.additional_monitoring_emails = additional_emails
        instance.enable_monitoring()

        # Check that the monitors have been created
        mock_newrelic.delete_synthetics_monitor.assert_not_called()
        mock_newrelic.create_synthetics_monitor.assert_has_calls([
            call(instance.url),
            call(instance.studio_url),
            call(instance.lms_preview_url),
            call(instance.lms_extended_heartbeat_url),
        ], any_order=True)
        self.assertCountEqual(
            instance.new_relic_availability_monitors.values_list('pk', flat=True),
            monitor_ids
        )

        # Check that alert emails have been set up
        created_condition_urls = set()
        created_condition_names = set()
        list_of_emails_added = []

        for add_call in mock_newrelic.add_alert_nrql_condition.call_args_list:
            created_condition_urls.add(add_call[0][1])
            created_condition_names.add(add_call[0][2])

        for add_call in mock_newrelic.add_email_notification_channel.call_args_list:
            list_of_emails_added.append(add_call[0][0])

        for condition_name in created_condition_names:
            self.assertTrue(condition_name.endswith("..."))
            self.assertLessEqual(len(condition_name), 64)

        self.assertEqual(created_condition_urls, set([
            instance.url,
            instance.studio_url,
            instance.lms_preview_url,
            instance.lms_extended_heartbeat_url,
        ]))
        self.assertEqual(set(list_of_emails_added), set(expected_emails))
        mock_newrelic.add_notification_channels_to_policy.assert_called_with(1, list(range(len(expected_emails))))
示例#19
0
    def test_update_monitoring_additional_email(self, mock_newrelic,
                                                mock_consul):
        """
        Check that the `enable_monitoring` method will add new
        'additional_monitoring_emails' to the existing monitors.
        """
        instance = OpenEdXInstanceFactory()
        existing_monitor_ids = [str(uuid4()) for i in range(4)]
        existing_monitors = {}
        existing_monitor_urls = [
            instance.url, instance.studio_url, instance.lms_preview_url,
            instance.lms_extended_heartbeat_url
        ]
        for i in range(4):
            new_id = existing_monitor_ids[i]
            instance.new_relic_availability_monitors.create(pk=new_id)
            existing_monitors[new_id] = {
                'id': new_id,
                'uri': existing_monitor_urls[i]
            }

        def mock_get_synthetics_monitor(monitor_id):
            """ Mock for get_synthetics_monitor() """
            return existing_monitors[monitor_id]

        ssl_monitor_ids = [str(uuid4()) for i in range(4)]
        mock_newrelic.create_synthetics_ssl_monitor.side_effect = ssl_monitor_ids
        mock_newrelic.get_synthetics_monitor.side_effect = mock_get_synthetics_monitor
        mock_newrelic.get_synthetics_notification_emails.return_value = [
            '*****@*****.**'
        ]
        mock_newrelic.add_alert_policy.return_value = 1
        mock_newrelic.add_alert_policy.return_value = 1
        mock_newrelic.add_alert_nrql_condition.side_effect = list(range(8))
        mock_newrelic.add_email_notification_channel.return_value = 1
        instance.new_relic_alert_policy = NewRelicAlertPolicy.objects.create(
            id=1, instance=instance)
        instance.new_relic_alert_policy.email_notification_channels.add(
            NewRelicEmailNotificationChannel.objects.create(
                id=0, email='*****@*****.**'))
        NewRelicEmailNotificationChannel.objects.create(
            id=10, email='*****@*****.**')
        instance.additional_monitoring_emails = [
            '*****@*****.**', '*****@*****.**'
        ]
        instance.enable_monitoring()

        # Check that the extra email has been added to existing monitors,
        # which should be unchanged
        mock_newrelic.create_synthetics_monitor.assert_not_called()
        mock_newrelic.delete_synthetics_monitor.assert_not_called()
        mock_newrelic.add_email_notification_channel.assert_called_with(
            '*****@*****.**')
        mock_newrelic.add_notification_channels_to_policy.assert_called_with(
            1, [1, 10])
示例#20
0
    def test_check_security_groups(self, mock_sync_security_group_rules,
                                   mock_get_openstack_connection, mock_consul):
        """
        Test that check_security_groups() can create and synchronize security groups
        """
        # We simulate the existence of these network security groups on the OpenStack cloud:
        existing_groups = ["group_a", "group_b"]
        new_security_group = Mock()

        def mocked_find_security_group(name_or_id):
            """ Mock openstack network.find_security_group """
            if name_or_id in existing_groups:
                result = Mock()
                result.name = name_or_id
                return result
            else:
                return None

        def mocked_create_security_group(**args):
            """ Mock openstack network.create_security_group """
            new_security_group.__dict__.update(**args)
            return new_security_group

        network = mock_get_openstack_connection().network
        network.find_security_group.side_effect = mocked_find_security_group
        network.create_security_group.side_effect = mocked_create_security_group

        instance = OpenEdXInstanceFactory(
            additional_security_groups=["group_a", "group_b"])
        app_server = make_test_appserver(instance)

        # Call check_security_groups():
        app_server.check_security_groups()
        # the default group doesn't exist, so we expect it was created:
        network.create_security_group.assert_called_once_with(
            name=settings.OPENEDX_APPSERVER_SECURITY_GROUP_NAME)
        # we also expect that its description was set:
        expected_description = "Security group for Open EdX AppServers. Managed automatically by OpenCraft IM."
        network.update_security_group.assert_called_once_with(
            new_security_group, description=expected_description)
        # We expect that the group was synced with the configured rules:
        mock_sync_security_group_rules.assert_called_once_with(
            new_security_group,
            OPENEDX_APPSERVER_SECURITY_GROUP_RULES,
            network=network)

        # Now, if we change the additional groups, we expect to get an exception:
        instance.additional_security_groups = ["invalid"]
        instance.save()
        app_server = make_test_appserver(instance)
        with self.assertRaisesRegex(
                Exception,
                "Unable to find the OpenStack network security group called 'invalid'."
        ):
            app_server.check_security_groups()
 def test_provision_mysql_no_mysql_server(self, mock_consul):
     """
     Don't provision a mysql database if instance has no MySQL server
     """
     self.instance = OpenEdXInstanceFactory()
     self.instance.mysql_server = None
     self.instance.save()
     self.instance.provision_mysql()
     databases = subprocess.check_output("mysql -h 127.0.0.1 -u root -e 'SHOW DATABASES'", shell=True).decode()
     for database in self.instance.mysql_databases:
         self.assertNotIn(database["name"], databases)
示例#22
0
 def test_get_permission_denied(self, username):
     """
     GET - basic and staff users denied access
     """
     self.api_client.login(username=username, password='******')
     OpenEdXInstanceFactory()
     response = self.api_client.get('/api/v1/instance/')
     self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
     self.assertEqual(
         response.data,
         {"detail": "You do not have permission to perform this action."})
示例#23
0
 def test_instance_list_user_no_organization(self):
     """
     Instance list should be empty if user doesn't belong to an organization.
     """
     instance = OpenEdXInstanceFactory(sub_domain='test.com')
     instance.ref.owner = self.organization
     instance.save()
     # User 5 doesn't belong to any organization
     self.api_client.login(username='******', password='******')
     response = self.api_client.get('/api/v1/instance/')
     self.assertEqual(len(response.data), 0)
示例#24
0
 def test_instance_list_non_admin_different_org(self):
     """
     A non-admin user shouldn't see an instance which belongs to a different organization.
     """
     instance = OpenEdXInstanceFactory(sub_domain='test.com')
     # User 4 belongs to organization 2, but instance belongs to organization 1
     instance.ref.owner = self.organization
     instance.save()
     self.api_client.login(username='******', password='******')
     response = self.api_client.get('/api/v1/instance/')
     self.assertEqual(len(response.data), 0)
示例#25
0
    def test_shut_down_obsolete_pr_sandboxes_no_pr(self, mock_archive):
        """
        Test that `shut_down_obsolete_pr_sandboxes` does not shut down instances
        that are not associated with a PR.
        """
        for dummy in range(5):
            OpenEdXInstanceFactory()

        tasks.shut_down_obsolete_pr_sandboxes()

        self.assertEqual(mock_archive.call_count, 0)
示例#26
0
def make_test_appserver(instance=None,
                        s3=False,
                        server=None,
                        organization=None,
                        status=None):  # noqa: MC0001
    """
    Factory method to create an OpenEdXAppServer (and OpenStackServer).

    Note that this method does not set the status of the VM (OpenStackServer)
    that is associated with the app server.
    Client code is expected to take care of that itself (if necessary).

    :param instance: The OpenEdx instance to create an AppServer for, if not
                     given will create a new instance.
    :param s3: Will configure S3 storage for the OpenEdXInstance the AppServer
               belongs to.
    :param server: The OpenStackServer to associate with this AppServer.
    :param organization: The organization that owns this AppServer.
    :param status: Will move an AppServer to the specified state
    :return: appserver for `instance`
    """
    if not instance:
        instance = OpenEdXInstanceFactory()
    if not instance.load_balancing_server:
        instance.load_balancing_server = LoadBalancingServer.objects.select_random(
        )
        instance.save()
    if s3:
        instance.storage_type = 's3'
        instance.s3_access_key = 'test'
        instance.s3_secret_access_key = 'test'
        instance.s3_bucket_name = 'test'
        instance.save()
    if organization:
        instance.ref.owner = organization
        instance.ref.save()
    appserver = instance._create_owned_appserver()

    if server:
        appserver.server = server
        appserver.save()

    if status == AppServerStatus.Running:
        _set_appserver_running(appserver)
    elif status == AppServerStatus.ConfiguringServer:
        _set_appserver_configuring_server(appserver)
    elif status == AppServerStatus.ConfigurationFailed:
        _set_appserver_configuration_failed(appserver)
    elif status == AppServerStatus.Error:
        _set_appserver_errored(appserver)
    elif status == AppServerStatus.Terminated:
        _set_appserver_terminated(appserver)

    return appserver
 def test_additional_security_groups(self, mocks):
     """
     Test a differently-named default security group, as well as the ability
     for an Instance to specify additional security groups.
     """
     instance = OpenEdXInstanceFactory(additional_security_groups=["group_a", "group_b"])
     app_server = make_test_appserver(instance)
     result = app_server.provision()
     self.assertTrue(result)
     create_server_kwargs = mocks.mock_create_server.call_args[1]
     self.assertEqual(create_server_kwargs["security_groups"], ["default-group", "group_a", "group_b"])
示例#28
0
 def test_get_logs_permission_denied(self, username, message):
     """
     GET - Basic users and anonymous can't get log entries
     """
     if username:
         self.api_client.login(username=username, password='******')
     instance = OpenEdXInstanceFactory()
     response = self.api_client.get(
         '/api/v1/instance/{pk}/logs/'.format(pk=instance.ref.pk))
     self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
     self.assertEqual(response.data, {'detail': message})
示例#29
0
 def get_instance_charges_future_month(self, mock_consul):
     """
     Test that the instance will return empty charges and 0 total for its
     AppServers if the invoice month is in future.
     """
     invoice_month = self._generate_invoice_date(year=datetime.now().year +
                                                 1)
     appservers_charges, appservers_total = get_instance_charges(
         OpenEdXInstanceFactory(), invoice_month)
     self.assertEqual(appservers_charges, [])
     self.assertEqual(appservers_total, 0)
 def test_preliminary_page_ip_address_configured(self, mock_consul):
     """
     Test that the preliminary page server IP is included in the load balancer configuration.
     """
     instance = OpenEdXInstanceFactory()
     _, [(_, config)
         ] = instance.get_preliminary_page_config(instance.ref.pk)
     self.assertIn(
         "server preliminary-page {}:443 ssl verify required ca-file /etc/ssl/certs/ca-certificates.crt"
         .format(settings.PRELIMINARY_PAGE_SERVER_IP), config)
     self.assertNotIn('http-request set-header Host', config)