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
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, {})
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, {})
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
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)
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)
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)
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
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)
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)
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]
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)
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)
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)
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)
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)
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)
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)
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)
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_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)
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})
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)
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})
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, {})
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, {})
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)
def setUp(self): """ Keep valid payload """ self.server = group_examples.launch_server_config()[0]['args']['server']
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)
def setUp(self): """ Store launch configuration """ self.launch = group_examples.launch_server_config()[0]