예제 #1
0
    def create_and_view_scaling_group(self):
        """
        Creating a scaling group with a valid config returns with a 200 OK and
        a Location header pointing to the new scaling group.

        :return: the path to the new scaling group resource
        """
        request_body = {
            "groupConfiguration": self.config,
            "launchConfiguration": launch_server_config()[0]
        }
        wrapper = self.successResultOf(
            request(root,
                    'POST',
                    '/v1.0/11111/groups/',
                    body=json.dumps(request_body)))

        self.assertEqual(wrapper.response.code, 201,
                         "Create failed: {0}".format(wrapper.content))
        response = json.loads(wrapper.content)
        for key in request_body:
            self.assertEqual(response["group"][key], request_body[key])
        for key in ("id", "links"):
            self.assertTrue(key in response["group"])

        headers = wrapper.response.headers.getRawHeaders('Location')
        self.assertTrue(headers is not None)
        self.assertEqual(1, len(headers))

        # now make sure the Location header points to something good!
        path = _strip_base_url(headers[0])

        wrapper = self.successResultOf(request(root, 'GET', path))
        self.assertEqual(wrapper.response.code, 200, path)

        response = json.loads(wrapper.content)
        self.assertEqual(response["group"]['groupConfiguration'], self.config)
        self.assertEqual(response["group"]['launchConfiguration'],
                         launch_server_config()[0])

        # make sure the created group has enough pending entities, and is
        # not paused
        wrapper = self.successResultOf(request(root, 'GET', path + 'state/'))
        self.assertEqual(wrapper.response.code, 200)

        response = json.loads(wrapper.content)
        self.assertTrue(not response["group"]['paused'])

        return path
예제 #2
0
    def setUp(self):
        """
        Replace the store every time with a clean one.
        """
        store = MockScalingGroupCollection()
        self.mock_log = mock.MagicMock()
        manifest = self.successResultOf(
            store.create_scaling_group(self.mock_log, self.tenant_id,
                                       config()[0],
                                       launch_server_config()[0]))
        self.group_id = manifest['id']
        set_store(store)

        self.policies_url = '/v1.0/{tenant}/groups/{group}/policies/'.format(
            tenant=self.tenant_id, group=self.group_id)

        controller_patcher = mock.patch('otter.rest.policies.controller')
        self.mock_controller = controller_patcher.start()
        self.mock_controller.maybe_execute_scaling_policy.return_value = defer.succeed(
            GroupState(self.tenant_id, self.group_id, {}, {}, 'date', {},
                       False))
        self.addCleanup(controller_patcher.stop)

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})
예제 #3
0
    def setUp(self):
        """
        Replace the store every time with a clean one.
        """
        store = MockScalingGroupCollection()
        self.mock_log = mock.MagicMock()
        manifest = self.successResultOf(
            store.create_scaling_group(self.mock_log, self.tenant_id, config()[0],
                                       launch_server_config()[0]))
        self.group_id = manifest['state'].group_id
        self.group_name = 'name'

        self.policies_url = '/v1.0/{tenant}/groups/{group}/policies/'.format(
            tenant=self.tenant_id, group=self.group_id)

        controller_patcher = mock.patch('otter.rest.policies.controller')
        self.mock_controller = controller_patcher.start()
        self.mock_controller.maybe_execute_scaling_policy.return_value = defer.succeed(
            GroupState(self.tenant_id, self.group_id, self.group_name, {}, {}, 'date', {}, False))
        self.addCleanup(controller_patcher.stop)

        self.root = Otter(store).app.resource()

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})
예제 #4
0
    def setUp(self):
        """
        Set up a silverberg client
        """
        keyspace.resume()
        set_store(store)  # ensure it's the cassandra store

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})

        self._config = config()[0]
        self._launch = launch_server_config()[0]

        self.mock_controller = patch(self, 'otter.rest.policies.controller')

        self.lock = self.mock_lock()
        patch(self, 'otter.models.cass.BasicLock', return_value=self.lock)

        def _set_group_id(manifest):
            self.group_id = manifest['id']
            self.policies_url = (
                '/v1.0/{tenant}/groups/{group}/policies/'.format(
                    tenant=self.tenant_id, group=self.group_id))
            self.mock_controller.maybe_execute_scaling_policy.return_value = defer.succeed(
                GroupState(self.tenant_id, self.group_id, {}, {}, 'date', {},
                           False))

        mock_log = mock.MagicMock()
        d = store.create_scaling_group(mock_log, self.tenant_id, self._config,
                                       self._launch)
        d.addCallback(_set_group_id)
        return d
예제 #5
0
    def setUp(self):
        """ Setup the mocks """
        set_config_data({'limits': {'absolute': {'maxGroups': 10,
                                                 'maxWebhooksPerPolicy': 10,
                                                 'maxPoliciesPerGroup': 10}}})
        self.addCleanup(set_config_data, {})

        self.collection = MockScalingGroupCollection()
        self.tenant_id = 'goo1234'
        self.config = {
            'name': 'blah',
            'cooldown': 600,
            'minEntities': 0,
            'maxEntities': 10,
            'metadata': {}
        }
        self.launch = group_examples.launch_server_config()[1]
        self.mock_log = mock.MagicMock()

        self.counter = 0

        def generate_uuid():
            self.counter += 1
            return self.counter

        self.mock_uuid = patch(self, 'otter.models.mock.uuid4',
                               side_effect=generate_uuid)
예제 #6
0
    def setUp(self):
        """
        Set up a silverberg client
        """
        keyspace.resume()
        set_store(store)  # ensure it's the cassandra store

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})

        self._config = config()[0]
        self._launch = launch_server_config()[0]

        self.mock_controller = patch(self, 'otter.rest.policies.controller')

        self.lock = self.mock_lock()
        patch(self, 'otter.models.cass.BasicLock', return_value=self.lock)

        def _set_group_id(manifest):
            self.group_id = manifest['id']
            self.policies_url = (
                '/v1.0/{tenant}/groups/{group}/policies/'.format(
                    tenant=self.tenant_id, group=self.group_id))
            self.mock_controller.maybe_execute_scaling_policy.return_value = defer.succeed(
                GroupState(self.tenant_id, self.group_id, {}, {}, 'date', {}, False))

        mock_log = mock.MagicMock()
        d = store.create_scaling_group(mock_log, self.tenant_id,
                                       self._config, self._launch)
        d.addCallback(_set_group_id)
        return d
예제 #7
0
 def test_no_metadata_valid(self):
     """
     No metadata in launch config is valid
     """
     config = deepcopy(group_examples.launch_server_config()[0])
     del config['args']['server']['metadata']
     validate(config, group_schemas.launch_server)
예제 #8
0
 def test_invalid_load_balancer_does_not_validate(self):
     """
     Cloud Load Balancers need a load balancer ID and a port, plus
     optionally a type.  RCv3 needs the type but not the port.
     The type needs to be valid.
     """
     base = group_examples.launch_server_config()[0]
     invalids = [
         {'loadBalancerId': '', 'port': 80},
         {'loadBalancerId': 3, 'port': '80'},
         {'loadBalancerId': 3, 'type': 'CloudLoadBalancer'},
         {'loadBalancerId': 3, 'port': '80', 'type': 'blah blah'},
         {'loadBalancerId': 3, 'type': 'RackConnectV3'},
         {'loadBalancerId': 'd6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2',
          'type': 'CloudLoadBalancer'},
         {'loadBalancerId': 'd6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2',
          'type': 'RackConnectV3', 'port': 80},
         {'loadBalancerId': 'd6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2'},
         {'loadBalancerId': 'd6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2',
          'type': ''},
         {'loadBalancerId': 'd6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2',
          'type': None},
         {'loadBalancerId': '112345', 'type': '', 'port': 80},
         {'loadBalancerId': '112345', 'type': None, 'port': 80},
     ]
     for invalid in invalids:
         base["args"]["loadBalancers"] = [invalid]
         # the type fails ot validiate because of 'not of type'
         self.assertRaisesRegexp(ValidationError, 'not of type', validate,
                                 base, group_schemas.launch_server)
         # because the type schema fails to validate, the config schema
         # fails to validate because it is not the given type
         self.assertRaisesRegexp(ValidationError, 'not of type', validate,
                                 base, group_schemas.launch_config)
예제 #9
0
    def setUp(self):
        """
        Set up a silverberg client
        """
        keyspace.resume()
        self.root = Otter(store, 'ord').app.resource()
        store.kz_client = mock.Mock(Lock=self.mock_lock())

        set_config_data(limits)
        self.addCleanup(set_config_data, {})

        self._config = config()[0]
        self._launch = launch_server_config()[0]

        self.mock_controller = patch(self, 'otter.rest.policies.controller')

        def _set_group_id(manifest):
            self.group_id = manifest['state'].group_id
            self.policies_url = (
                '/v1.0/{tenant}/groups/{group}/policies/'.format(
                    tenant=self.tenant_id, group=self.group_id))
            self.mock_controller.maybe_execute_scaling_policy.return_value = defer.succeed(
                GroupState(self.tenant_id, self.group_id,
                           self._config['name'], {}, {}, 'date', {}, False))

        mock_log = mock.MagicMock()
        d = store.create_scaling_group(mock_log, self.tenant_id,
                                       self._config, self._launch)
        d.addCallback(_set_group_id)
        return d
    def create_and_view_scaling_group(self):
        """
        Creating a scaling group with a valid config returns with a 200 OK and
        a Location header pointing to the new scaling group.

        :return: the path to the new scaling group resource
        """
        request_body = {
            "groupConfiguration": self.config,
            "launchConfiguration": launch_server_config()[0]
        }
        wrapper = self.successResultOf(request(
            root, 'POST', '/v1.0/11111/groups/', body=json.dumps(request_body)))

        self.assertEqual(wrapper.response.code, 201,
                         "Create failed: {0}".format(wrapper.content))
        response = json.loads(wrapper.content)
        for key in request_body:
            self.assertEqual(response["group"][key], request_body[key])
        for key in ("id", "links"):
            self.assertTrue(key in response["group"])

        headers = wrapper.response.headers.getRawHeaders('Location')
        self.assertTrue(headers is not None)
        self.assertEqual(1, len(headers))

        # now make sure the Location header points to something good!
        path = _strip_base_url(headers[0])

        wrapper = self.successResultOf(request(root, 'GET', path))
        self.assertEqual(wrapper.response.code, 200, path)

        response = json.loads(wrapper.content)
        self.assertEqual(response["group"]['groupConfiguration'], self.config)
        self.assertEqual(response["group"]['launchConfiguration'],
                         launch_server_config()[0])

        # make sure the created group has enough pending entities, and is
        # not paused
        wrapper = self.successResultOf(
            request(root, 'GET', path + 'state/'))
        self.assertEqual(wrapper.response.code, 200)

        response = json.loads(wrapper.content)
        self.assertTrue(not response["group"]['paused'])

        return path
예제 #11
0
 def test_special_char_metadata_invalid(self):
     """
     metadata with special character in launch config is valid
     """
     invalid = deepcopy(group_examples.launch_server_config()[0])
     invalid['args']['server']['metadata'] = {'anc%': 'ag'}
     self.assertRaises(ValidationError, validate, invalid,
                       group_schemas.launch_server)
예제 #12
0
 def test_draining_nan(self):
     """
     Draining timeout must be a number
     """
     lc = deepcopy(group_examples.launch_server_config()[0])
     lc["args"]["draining_timeout"] = "string"
     self.assertRaises(ValidationError, validate, lc,
                       group_schemas.launch_server)
예제 #13
0
 def setUp(self):
     """
     Set up a reference to a standard, valid policy, config, and launch
     configs.
     """
     self.policy = group_examples.policy()[0]
     self.config = group_examples.config()[0]
     self.launch = group_examples.launch_server_config()[0]
예제 #14
0
 def setUp(self):
     """
     Set up a reference to a standard, valid policy, config, and launch
     configs.
     """
     self.policy = group_examples.policy()[0]
     self.config = group_examples.config()[0]
     self.launch = group_examples.launch_server_config()[0]
예제 #15
0
 def test_array_metadata_invalid(self):
     """
     metadata with array in it is not allowed
     """
     config = deepcopy(group_examples.launch_server_config()[0])
     config['args']['server']['metadata'] = []
     self.assertRaisesRegexp(
         ValidationError, "\[\] is not of type ",
         validate, config, group_schemas.launch_server)
예제 #16
0
 def test_draining_minmax(self):
     """
     Draining timeout must be >= 30 and <= 3600
     """
     lc = deepcopy(group_examples.launch_server_config()[0])
     lc["args"]["draining_timeout"] = 20
     self.assertRaises(ValidationError, validate, lc,
                       group_schemas.launch_server)
     lc["args"]["draining_timeout"] = 3601
     self.assertRaises(ValidationError, validate, lc,
                       group_schemas.launch_server)
예제 #17
0
    def test_more255_metadata_invalid(self):
        """
        metadata with key or value > 255 in launch config is valid
        """
        invalid = deepcopy(group_examples.launch_server_config()[0])
        invalid['args']['server']['metadata'] = {'ab' * 150: 'ag'}
        self.assertRaises(ValidationError, validate, invalid,
                          group_schemas.launch_server)

        invalid['args']['server']['metadata'] = {'ab': 'ag' * 150}
        self.assertRaises(ValidationError, validate, invalid,
                          group_schemas.launch_server)
예제 #18
0
파일: test_schemas.py 프로젝트: alex/otter
 def test_wrong_scaling_policy_fails(self):
     """
     An otherwise ok creation blob fails if the provided scaling policies
     are wrong.
     """
     self.assertRaises(
         ValidationError, validate, {
             'groupConfiguration': group_examples.config()[0],
             'launchConfiguration':
             group_examples.launch_server_config()[0],
             'scalingPolicies': {"Hello!": "Yes quite."}
         }, rest_schemas.create_group_request)
예제 #19
0
파일: test_schemas.py 프로젝트: alex/otter
 def test_wrong_group_config_fails(self):
     """
     Not including a groupConfiguration or including an invalid ones will
     fail to validate.
     """
     invalid = {'launchConfiguration':
                group_examples.launch_server_config()[0]}
     self.assertRaisesRegexp(
         ValidationError, 'groupConfiguration',
         validate, invalid, rest_schemas.create_group_request)
     invalid['groupConfiguration'] = {}
     self.assertRaises(ValidationError,
                       validate, invalid, rest_schemas.create_group_request)
예제 #20
0
    def test_too_many_load_balancers_do_not_validate(self):
        """
        If more than 5 load balancers are provided, the launch config fails to
        validate.
        """
        invalid = group_examples.launch_server_config()[0]
        invalid['args']["loadBalancers"] = [{'loadBalancerId': i, 'port': 80}
                                            for i in range(6)]

        # the type fails ot valdiate because the load balancer list is too long
        self.assertRaisesRegexp(ValidationError, 'is too long',
                                validate, invalid, group_schemas.launch_server)
        # because the type schema fails to validate, the config schema
        # fails to validate because it is not the given type
        self.assertRaisesRegexp(ValidationError, 'not of type',
                                validate, invalid, group_schemas.launch_config)
예제 #21
0
 def test_duplicate_load_balancers_do_not_validate(self):
     """
     If the same load balancer config appears twice, the launch config
     fails to validate.
     """
     invalid = group_examples.launch_server_config()[0]
     invalid['args']["loadBalancers"] = [
         {'loadBalancerId': 1, 'port': 80},
         {'loadBalancerId': 1, 'port': 80}
     ]
     # the type fails ot valdiate because of the load balancers are not
     # unique
     self.assertRaisesRegexp(ValidationError, 'non-unique elements',
                             validate, invalid, group_schemas.launch_server)
     # because the type schema fails to validate, the config schema
     # fails to validate because it is not the given type
     self.assertRaisesRegexp(ValidationError, 'not of type',
                             validate, invalid, group_schemas.launch_config)
예제 #22
0
    def setUp(self):
        """
        Create a mock group
        """
        set_config_data({'limits': {'absolute': {'maxWebhooksPerPolicy': 10,
                                                 'maxPoliciesPerGroup': 10}}})
        self.addCleanup(set_config_data, {})

        self.tenant_id = '11111'
        self.group_id = '1'
        self.mock_log = mock.MagicMock()
        self.collection = mock.MagicMock(spec=[], data={self.tenant_id: {}})

        self.config = {
            'name': 'aname',
            'cooldown': 0,
            'minEntities': 0
        }
        # this is the config with all the default vals
        self.output_config = {
            'name': 'aname',
            'cooldown': 0,
            'minEntities': 0,
            'maxEntities': None,
            'metadata': {}
        }
        self.launch_config = group_examples.launch_server_config()[0]
        self.policies = group_examples.policy()[:1]
        self.group = MockScalingGroup(
            self.mock_log, self.tenant_id, self.group_id, self.collection,
            {'config': self.config, 'launch': self.launch_config,
             'policies': self.policies})

        self.collection.data[self.tenant_id]['1'] = self.group

        self.counter = 0

        def generate_uuid():
            self.counter += 1
            return self.counter

        self.mock_uuid = patch(self, 'otter.models.mock.uuid4',
                               side_effect=generate_uuid)
예제 #23
0
    def test_update_launch_config(self):
        """
        Editing the launch config of a scaling group with a valid launch config
        returns with a 204 no content.  The next attempt to view the launch
        config should return the new launch config.
        """
        path = yield self.create_scaling_group()
        launch_path = path + 'launch/'
        edited_launch = launch_server_config()[1]

        wrapper = yield request(root, 'PUT',
                                launch_path, body=json.dumps(edited_launch))
        self.assert_response(wrapper, 204, "Edit launch config failed.")
        self.assertEqual(wrapper.content, "")

        # now try to view again - the config should be the edited config
        wrapper = yield request(root, 'GET', launch_path)
        self.assert_response(wrapper, 200)
        self.assertEqual(json.loads(wrapper.content),
                         {'launchConfiguration': edited_launch})
예제 #24
0
 def test_unspecified_args_do_not_validate(self):
     """
     If random attributes to args are provided, the launch config fails to
     validate
     """
     server = group_examples.launch_server_config()[0]['args']['server']
     invalid = {
         "type": "launch_server",
         "args": {
             "server": server,
             "hat": "top"
         }
     }
     # the type fails ot valdiate because of the additional 'hat' property
     self.assertRaisesRegexp(ValidationError, 'Additional properties',
                             validate, invalid, group_schemas.launch_server)
     # because the type schema fails to validate, the config schema
     # fails to validate because it is not the given type
     self.assertRaisesRegexp(ValidationError, 'not of type',
                             validate, invalid, group_schemas.launch_config)
예제 #25
0
    def test_ru_launch_config(self):
        """
        Editing the launch config of a scaling group with a valid launch config
        returns with a 204 no content.  The next attempt to view the launch
        config should return the new launch config.
        """
        # make sure there is a scaling group
        path = self.create_and_view_scaling_group() + 'launch/'
        edited_launch = launch_server_config()[1]

        wrapper = self.successResultOf(
            request(root, 'PUT', path, body=json.dumps(edited_launch)))

        self.assertEqual(wrapper.response.code, 204,
                         "Edit failed: {0}".format(wrapper.content))
        self.assertEqual(wrapper.content, "")

        # now try to view again - the config should be the edited config
        wrapper = self.successResultOf(request(root, 'GET', path))
        self.assertEqual(wrapper.response.code, 200)
        self.assertEqual(json.loads(wrapper.content),
                         {'launchConfiguration': edited_launch})
예제 #26
0
    def setUp(self):
        """ Setup the mocks """
        self.collection = MockScalingGroupCollection()
        self.tenant_id = 'goo1234'
        self.config = {
            'name': 'blah',
            'cooldown': 600,
            'minEntities': 0,
            'maxEntities': 10,
            'metadata': {}
        }
        self.launch = group_examples.launch_server_config()[1]
        self.mock_log = mock.MagicMock()

        self.counter = 0

        def generate_uuid():
            self.counter += 1
            return self.counter

        self.mock_uuid = patch(self, 'otter.models.mock.uuid4',
                               side_effect=generate_uuid)
예제 #27
0
    def test_update_launch_config(self):
        """
        Editing the launch config of a scaling group with a valid launch config
        returns with a 204 no content.  The next attempt to view the launch
        config should return the new launch config.
        """
        path = yield self.create_scaling_group()
        launch_path = path + 'launch/'
        edited_launch = launch_server_config()[1]

        wrapper = yield request(root,
                                'PUT',
                                launch_path,
                                body=json.dumps(edited_launch))
        self.assert_response(wrapper, 204, "Edit launch config failed.")
        self.assertEqual(wrapper.content, "")

        # now try to view again - the config should be the edited config
        wrapper = yield request(root, 'GET', launch_path)
        self.assert_response(wrapper, 200)
        self.assertEqual(json.loads(wrapper.content),
                         {'launchConfiguration': edited_launch})
    def test_ru_launch_config(self):
        """
        Editing the launch config of a scaling group with a valid launch config
        returns with a 204 no content.  The next attempt to view the launch
        config should return the new launch config.
        """
        # make sure there is a scaling group
        path = self.create_and_view_scaling_group() + 'launch/'
        edited_launch = launch_server_config()[1]

        wrapper = self.successResultOf(
            request(root, 'PUT', path, body=json.dumps(edited_launch)))

        self.assertEqual(wrapper.response.code, 204,
                         "Edit failed: {0}".format(wrapper.content))
        self.assertEqual(wrapper.content, "")

        # now try to view again - the config should be the edited config
        wrapper = self.successResultOf(
            request(root, 'GET', path))
        self.assertEqual(wrapper.response.code, 200)
        self.assertEqual(json.loads(wrapper.content),
                         {'launchConfiguration': edited_launch})
예제 #29
0
    def setUp(self):
        """ Setup the mocks """
        self.collection = MockScalingGroupCollection()
        self.tenant_id = 'goo1234'
        self.config = {
            'name': 'blah',
            'cooldown': 600,
            'minEntities': 0,
            'maxEntities': 10,
            'metadata': {}
        }
        self.launch = group_examples.launch_server_config()[1]
        self.mock_log = mock.MagicMock()

        self.counter = 0

        def generate_uuid():
            self.counter += 1
            return self.counter

        self.mock_uuid = patch(self,
                               'otter.models.mock.uuid4',
                               side_effect=generate_uuid)
예제 #30
0
    def setUp(self):
        """
        Replace the store every time with a clean one.
        """
        self.mock_log = mock.MagicMock()
        store = MockScalingGroupCollection()
        manifest = self.successResultOf(
            store.create_scaling_group(self.mock_log, self.tenant_id,
                                       config()[0],
                                       launch_server_config()[0]))
        self.group_id = manifest['state'].group_id
        group = store.get_scaling_group(self.mock_log,
                                        self.tenant_id, self.group_id)
        self.policy_id = self.successResultOf(
            group.create_policies([{
                "name": 'set number of servers to 10',
                "change": 10,
                "cooldown": 3,
                "type": "webhook"
            }]))[0]['id']

        self.webhooks_url = (
            '/v1.0/{tenant}/groups/{group}/policies/{policy}/webhooks/'.format(
                tenant=self.tenant_id, group=self.group_id,
                policy=self.policy_id))

        self.mock_controller = patch(self, 'otter.rest.webhooks.controller')

        def _mock_maybe_execute(log, trans, group, state, policy_id):
            return defer.succeed(state)

        self.mock_controller.maybe_execute_scaling_policy.side_effect = _mock_maybe_execute

        self.root = Otter(store).app.resource()

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})
예제 #31
0
    def setUp(self):
        """
        Replace the store every time with a clean one.
        """
        self.mock_log = mock.MagicMock()
        store = MockScalingGroupCollection()
        manifest = self.successResultOf(
            store.create_scaling_group(self.mock_log, self.tenant_id,
                                       config()[0],
                                       launch_server_config()[0]))
        self.group_id = manifest['id']
        group = store.get_scaling_group(self.mock_log, self.tenant_id,
                                        self.group_id)
        self.policy_id = self.successResultOf(
            group.create_policies([{
                "name": 'set number of servers to 10',
                "change": 10,
                "cooldown": 3,
                "type": "webhook"
            }])).keys()[0]
        set_store(store)

        self.webhooks_url = (
            '/v1.0/{tenant}/groups/{group}/policies/{policy}/webhooks/'.format(
                tenant=self.tenant_id,
                group=self.group_id,
                policy=self.policy_id))

        self.mock_controller = patch(self, 'otter.rest.webhooks.controller')

        def _mock_maybe_execute(log, trans, group, state, policy_id):
            return defer.succeed(state)

        self.mock_controller.maybe_execute_scaling_policy.side_effect = _mock_maybe_execute

        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})
예제 #32
0
 def test_valid_examples_validate(self):
     """
     The launch server config examples all validate.
     """
     for example in group_examples.launch_server_config():
         validate(example, group_schemas.launch_config)
예제 #33
0
 def setUp(self):
     """
     Keep valid payload
     """
     self.server = group_examples.launch_server_config()[0]['args']['server']
예제 #34
0
class CassStoreRestScalingGroupTestCase(TestCase, RequestTestMixin, LockMixin):
    """
    Test case for testing the REST API for the scaling group specific endpoints
    (not policies or webhooks) against the Cassandra model.
    """

    _launch_server_config = launch_server_config()[0]
    _policies = _policy()

    def setUp(self):
        """
        Set the Cassandra store, and also patch the controller
        """
        keyspace.resume()
        set_store(store)
        set_config_data({'url_root': 'http://127.0.0.1'})
        self.addCleanup(set_config_data, {})

        self.config = config()[0]
        self.config['minEntities'] = 0
        self.active_pending_etc = ({}, {}, 'date', {}, False)

        # patch both the config and the groups
        self.mock_controller = patch(self,
                                     'otter.rest.configs.controller',
                                     spec=['obey_config_change'])
        patch(self, 'otter.rest.groups.controller', new=self.mock_controller)

        def _mock_obey_config_change(log, trans, config, group, state):
            return defer.succeed(
                GroupState(state.tenant_id, state.group_id,
                           *self.active_pending_etc))

        self.mock_controller.obey_config_change.side_effect = _mock_obey_config_change

        self.lock = self.mock_lock()
        patch(self, 'otter.models.cass.BasicLock', return_value=self.lock)

    def tearDown(self):
        """
        Disconnect the client - it will reconnect as needed.  Better if this
        could be disconnected only once this particular module were done.
        """
        keyspace.dirtied()
        keyspace.pause()
        keyspace.reset(self.mktemp())

    def create_scaling_group(self):
        """
        Creates a scaling group and returns the path.
        """
        def _check_create_response(wrapper):
            # the body is probably verified by the unit tests - check the
            # header and status code
            self.assert_response(wrapper, 201, "Create failed.")
            # now make sure the Location header points to something good!
            return path_only(self.get_location_header(wrapper))

        request_body = {
            "groupConfiguration": self.config,
            "launchConfiguration": self._launch_server_config,
            "scalingPolicies": self._policies
        }
        deferred = request(root,
                           'POST',
                           '/v1.0/11111/groups/',
                           body=json.dumps(request_body))
        deferred.addCallback(_check_create_response)
        return deferred

    def create_and_view_scaling_group(self):
        """
        Creating a scaling group with a valid config returns with a 200 OK and
        a Location header pointing to the new scaling group.

        :return: the path to the new scaling group resource
        """
        def _check_policies_created(wrapper):
            self.assert_response(wrapper, 200)
            response = json.loads(wrapper.content)
            self.assertEqual(len(response["policies"]), len(self._policies))

        def _check_creation_worked(path):
            # TODO: check manifest and state as well
            d = defer.gatherResults([
                request(root, 'GET', path +
                        'policies/').addCallback(_check_policies_created),
                self.assert_state(path, self.config['minEntities'], False)
            ])

            # no matter what, just return the path
            return d.addCallback(lambda _: path)

        deferred = self.create_scaling_group()
        deferred.addCallback(_check_creation_worked)
        return deferred

    def assert_state(self, path, entities, paused):
        """
        Assert that the state has the specified number of total entities and
        is or is not paused, as specified.

        :return: deferred that fires with None
        """
        def _check_state(wrapper):
            self.assertEqual(wrapper.response.code, 200)
            response = json.loads(wrapper.content)
            self.assertEqual(response['group']['paused'], paused)
            self.assertEqual(response['group']['desiredCapacity'], entities)

        return request(root, 'GET', path + 'state/').addCallback(_check_state)

    @defer.inlineCallbacks
    def delete_and_view_scaling_group(self, path):
        """
        Deleting a scaling group returns with a 204 no content.  The next
        attempt to view the scaling group should return a 404 not found.
        """
        wrapper = yield request(root, 'DELETE', path)
        self.assert_response(wrapper, 204, "Delete failed.")
        self.assertEqual(wrapper.content, "")

        # now try to view policies
        # TODO: view state and manifest too, once they have been implemented
        wrapper = yield request(root, 'GET', path + 'policies/')
        self.assert_response(wrapper, 404, "Deleted group still there.")

        # flush any logged errors
        self.flushLoggedErrors(NoSuchScalingGroupError)

    def assert_number_of_scaling_groups(self, number):
        """
        Asserts that there are ``number`` number of scaling groups
        """
        def _check_number(wrapper):
            self.assert_response(wrapper, 200)
            response = json.loads(wrapper.content)
            self.assertEqual(len(response["groups"]), number)

        d = request(root, 'GET', '/v1.0/11111/groups/')
        return d.addCallback(_check_number)

    @defer.inlineCallbacks
    def test_crd_scaling_group(self):
        """
        Start with no scaling groups.  Create one, make sure it's listed, then
        delete it and make sure it's no longer listed.
        """
        # start with no scaling groups
        yield self.assert_number_of_scaling_groups(0)
        path = yield self.create_and_view_scaling_group()

        # there should now be one scaling group
        yield self.assert_number_of_scaling_groups(1)
        yield self.delete_and_view_scaling_group(path)

        # there should be no scaling groups now
        yield self.assert_number_of_scaling_groups(0)

    @defer.inlineCallbacks
    def test_update_launch_config(self):
        """
        Editing the launch config of a scaling group with a valid launch config
        returns with a 204 no content.  The next attempt to view the launch
        config should return the new launch config.
        """
        path = yield self.create_scaling_group()
        launch_path = path + 'launch/'
        edited_launch = launch_server_config()[1]

        wrapper = yield request(root,
                                'PUT',
                                launch_path,
                                body=json.dumps(edited_launch))
        self.assert_response(wrapper, 204, "Edit launch config failed.")
        self.assertEqual(wrapper.content, "")

        # now try to view again - the config should be the edited config
        wrapper = yield request(root, 'GET', launch_path)
        self.assert_response(wrapper, 200)
        self.assertEqual(json.loads(wrapper.content),
                         {'launchConfiguration': edited_launch})

    @defer.inlineCallbacks
    def test_update_config(self):
        """
        Editing the config of a scaling group to one with a higher min returns
        204 with no content.  The state is updated to reflect existing at least
        the min number of pending + current
        """
        path = yield self.create_and_view_scaling_group()
        config_path = path + 'config/'

        self.config['minEntities'] = 2
        self.config['maxEntities'] = 25
        self.config['metadata'] = {}
        self.active_pending_etc = ({}, {'1': {}, '2': {}}, 'date', {}, False)

        wrapper = yield request(root,
                                'PUT',
                                config_path,
                                body=json.dumps(self.config))
        self.assert_response(wrapper, 204, "Edit config failed")
        self.assertEqual(wrapper.content, "")

        yield self.assert_state(path, 2, False)

    @defer.inlineCallbacks
    def test_create_scaling_group_with_min_entities(self):
        """
        Create a scaling group with >0 min entities calls obey config changes
        """
        self.config['minEntities'] = 2
        self.active_pending_etc = ({}, {'1': {}, '2': {}}, 'date', {}, False)

        path = yield self.create_scaling_group()
        yield self.assert_state(path, 2, False)
예제 #35
0
 def setUp(self):
     """
     Store launch configuration
     """
     self.launch = group_examples.launch_server_config()[0]
예제 #36
0
 def test_valid_examples_validate(self):
     """
     The launch server config examples all validate.
     """
     for example in group_examples.launch_server_config():
         validate(example, group_schemas.launch_config)