def setUp(self): self.sd = {"name": "test"} self.context = Context({"namespace": "namespace"}) self.stack = Stack( definition=generate_definition("vpc", 1), context=self.context, )
def setUp(self): self.sd = {"name": "test"} self.context = Context({'namespace': 'namespace'}) self.stack = Stack( definition=generate_definition('vpc', 1), context=self.context, )
def setUp(self): self.context = Context({'namespace': 'namespace'}) self.context.config = { 'stacks': [ { 'name': 'vpc' }, { 'name': 'bastion', 'requires': ['vpc'] }, { 'name': 'instance', 'requires': ['vpc', 'bastion'] }, { 'name': 'db', 'requires': ['instance', 'vpc', 'bastion'] }, { 'name': 'other', 'requires': ['db'] }, ], } self.action = destroy.Action(self.context, provider=mock.MagicMock())
def setUp(self): config = Config({ "namespace": "namespace", "stacks": [ { "name": "vpc" }, { "name": "bastion", "requires": ["vpc"] }, { "name": "instance", "requires": ["vpc", "bastion"] }, { "name": "db", "requires": ["instance", "vpc", "bastion"] }, { "name": "other", "requires": ["db"] }, ], }) self.context = Context(config=config) self.action = destroy.Action(self.context, cancel=MockThreadingEvent())
def setUp(self): config = Config({ "namespace": "namespace", "stacks": [ { "name": "vpc" }, { "name": "bastion", "requires": ["vpc"] }, { "name": "instance", "requires": ["vpc", "bastion"] }, { "name": "db", "requires": ["instance", "vpc", "bastion"] }, { "name": "other", "requires": ["db"] }, ], }) self.context = Context(config=config) self.action = destroy.Action(self.context, provider=mock.MagicMock())
def test_context_bucket_name_is_overriden_but_is_none(self): config = Config({"namespace": "test", "stacker_bucket": ""}) context = Context(config=config) self.assertEqual(context.bucket_name, None) config = Config({"namespace": "test", "stacker_bucket": None}) context = Context(config=config) self.assertEqual(context.bucket_name, "stacker-test")
def test_context_default_bucket_no_namespace(self): context = Context(config=Config({"namespace": ""})) self.assertEqual(context.bucket_name, None) context = Context(config=Config({"namespace": None})) self.assertEqual(context.bucket_name, None) context = Context( config=Config({"namespace": None, "stacker_bucket": ""})) self.assertEqual(context.bucket_name, None)
def mock_context(namespace="default", extra_config_args=None, **kwargs): config_args = {"namespace": namespace} if extra_config_args: config_args.update(extra_config_args) config = Config(config_args) if kwargs.get("environment"): return Context( config=config, **kwargs) return Context( config=config, environment={}, **kwargs)
def generate_sample_k8s_cfn_repo(env_root): """Generate sample k8s infrastructure repo.""" repo_dir = os.path.join(env_root, 'k8s-cfn-infrastructure') if os.path.isdir(repo_dir): LOGGER.error("Error generating sample repo -- directory %s " "already exists!", repo_dir) sys.exit(1) from runway.blueprints.k8s.k8s_master import Cluster from runway.blueprints.k8s.k8s_iam import Iam from runway.blueprints.k8s.k8s_workers import NodeGroup as WorkerNodeGroup shutil.copytree( os.path.join(ROOT, 'templates', 'k8s-cfn-repo'), repo_dir ) os.rename(os.path.join(repo_dir, '_gitignore'), os.path.join(repo_dir, '.gitignore')) # Generate masters CFN templates from blueprints master_template_dir = os.path.join(repo_dir, 'k8s-master.cfn', 'templates') os.mkdir(master_template_dir) with open(os.path.join(master_template_dir, 'k8s_iam.yaml'), 'w') as stream: stream.write(to_yaml(Iam('test', Context({"namespace": "test"}), None).to_json())) with open(os.path.join(master_template_dir, 'k8s_master.yaml'), 'w') as stream: stream.write(to_yaml(Cluster('test', Context({"namespace": "test"}), None).to_json())) # Generate workers CFN template from blueprint worker_template_dir = os.path.join(repo_dir, 'k8s-workers.cfn', 'templates') os.mkdir(worker_template_dir) with open(os.path.join(worker_template_dir, 'k8s_workers.yaml'), 'w') as stream: stream.write(to_yaml(WorkerNodeGroup('test', Context({"namespace": "test"}), None).to_json())) LOGGER.info("Sample k8s infrastructure repo created at %s", repo_dir) LOGGER.info('(see its README for setup and deployment instructions)')
def test_s3_static_website(self): """Test a static website blog bucket.""" ctx = Context(config=Config({'namespace': 'test'})) blueprint = Buckets('s3_static_website', ctx) v = self.variables = [ Variable( 'Buckets', { 'Blog': { 'AccessControl': 'PublicRead', 'WebsiteConfiguration': { 'IndexDocument': 'index.html' } }, }), Variable('ReadRoles', [ 'Role1', 'Role2', ]), Variable('ReadWriteRoles', [ 'Role3', 'Role4', ]), ] blueprint.resolve_variables(v) blueprint.create_template() self.assertRenderedBlueprint(blueprint)
def test_dynamodb_autoscaling(self): ctx = Context({'namespace': 'test', 'environment': 'test'}) blueprint = stacker_blueprints.dynamodb.AutoScaling( 'dynamodb_autoscaling', ctx) blueprint.resolve_variables(self.dynamodb_autoscaling_variables) blueprint.create_template() self.assertRenderedBlueprint(blueprint)
def construct_context(subnet_id): config_dict = { "namespace": "", "namespace_delimiter": "", "stacker_bucket": "", "bucket_region": "ap-southeast-2", "region": "ap-southeast-2", "stacks": [ { "name": "ebs-pin-test", "template_path": "./e2e/cloudformation.yml", "variables": { "AvailabilityZone": "ap-southeast-2a", "AMI": ami, "VpcId": vpc_id, "Subnets": subnet_id, "KeyName": "id_rsa" }, "tags": { "Name": "ebs-pin-test" } } ] } config = Config(config_dict) context = Context(config=config) return context
class TestHookDataLookup(unittest.TestCase): def setUp(self): self.ctx = Context({"namespace": "test-ns"}) self.ctx.set_hook_data("fake_hook", {"result": "good"}) def test_valid_hook_data(self): value = handler("fake_hook::result", context=self.ctx) self.assertEqual(value, "good") def test_invalid_hook_data(self): with self.assertRaises(KeyError): handler("fake_hook::bad_key", context=self.ctx) def test_bad_value_hook_data(self): with self.assertRaises(ValueError): handler("fake_hook", context=self.ctx)
def setUp(self): self.sd = {"name": "test"} self.context = Context('namespace') self.stack = Stack( definition=generate_definition('vpc', 1), context=self.context, )
def mock_context(namespace=None, **kwargs): config = Config({"namespace": namespace}) environment = kwargs.get("environment", {}) return Context( config=config, environment=environment, **kwargs)
def test_context_optional_keys_set(self): context = Context( config=Config({}), stack_names=["stack"], ) self.assertEqual(context.mappings, {}) self.assertEqual(context.stack_names, ["stack"])
def setUp(self): self.provider = MagicMock() self.context = Context( environment={ 'namespace': 'test', 'env_var': 'val_in_env'} )
def generate_tfstate_cfn_template(): """Return rendered CFN template yaml.""" from runway.blueprints.tf_state import TfState return to_yaml(TfState('test', Context({"namespace": "test"}), None).to_json())
class TestHookDataLookup(unittest.TestCase): def setUp(self): self.ctx = Context({"namespace": "test-ns"}) self.ctx.set_hook_data("fake_hook", {"result": "good"}) def test_valid_hook_data(self): value = HookDataLookup.handle("fake_hook::result", context=self.ctx) self.assertEqual(value, "good") def test_invalid_hook_data(self): with self.assertRaises(KeyError): HookDataLookup.handle("fake_hook::bad_key", context=self.ctx) def test_bad_value_hook_data(self): with self.assertRaises(ValueError): HookDataLookup.handle("fake_hook", context=self.ctx)
def setUp(self): self.ctx = Context(config=Config({'namespace': 'test'})) self.common_variables = { "VpcId": "vpc-abc1234", "VpcDefaultSecurityGroup": "sg-01234abc", "AvailabilityZone": "us-east-1a", "CidrBlock": "10.0.0.0/24", }
def setUp(self): self.common_variables = [ Variable("Instances", {"MyInstance": { "ImageId": "ami-abc12345", }}) ] self.ctx = Context({'namespace': 'test', 'environment': 'test'})
def setUp(self): self.ctx = Context(config=Config({'namespace': 'test'})) self.common_variables = { "VPC": { VPC_NAME: { "CidrBlock": "10.0.0.0/16" } } }
def setUp(self): self.sd = {"name": "test"} self.config = Config({"namespace": "namespace"}) self.context = Context(config=self.config) self.stack = Stack( definition=generate_definition("vpc", 1), context=self.context, ) register_lookup_handler("noop", lambda **kwargs: "test")
def _get_context(self, **kwargs): config = {"stacks": [ {"name": "vpc"}, {"name": "bastion", "parameters": {"test": "vpc::something"}}, {"name": "db", "parameters": {"test": "vpc::something", "else": "bastion::something"}}, {"name": "other", "parameters": {}} ]} return Context({"namespace": "namespace"}, config=config, **kwargs)
def setUp(self): self.common_variables = [ Variable( "SecurityGroups", {"MySG1": { "GroupDescription": "My first SecurityGroup", }}) ] self.ctx = Context({'namespace': 'test', 'environment': 'test'})
def setUp(self): self.context = Context({"namespace": "namespace"}) stack = Stack( definition=generate_definition("vpc", 1), context=self.context, ) self.step = Step( stack=stack, run_func=lambda x, y: (x, y), )
def setUp(self): self.context = Context({'namespace': 'namespace'}) self.context.config = { 'stacks': [ {'name': 'vpc'}, {'name': 'bastion', 'requires': ['vpc']}, {'name': 'instance', 'requires': ['vpc', 'bastion']}, {'name': 'db', 'requires': ['instance', 'vpc', 'bastion']}, {'name': 'other', 'requires': ['db']}, ], } self.action = destroy.Action(self.context, provider=mock.MagicMock())
def setUp(self): config = Config({ "namespace": "namespace", "stacks": [ {"name": "vpc"}, {"name": "bastion", "requires": ["vpc"]}, {"name": "instance", "requires": ["vpc", "bastion"]}, {"name": "db", "requires": ["instance", "vpc", "bastion"]}, {"name": "other", "requires": ["db"]}, ], }) self.context = Context(config=config) self.action = destroy.Action(self.context, cancel=MockThreadingEvent())
def _get_context(self, **kwargs): config = Config({ "namespace": "namespace", "stacks": [ {"name": "vpc"}, {"name": "bastion", "variables": {"test": "${output vpc::something}"}}, {"name": "db", "variables": {"test": "${output vpc::something}", "else": "${output bastion::something}"}}, {"name": "other", "variables": {}} ] }) return Context(config=config, **kwargs)
def setUp(self): self.code = Code(S3Bucket="test_bucket", S3Key="code_key") self.common_variables = { "Code": self.code, "DeadLetterArn": "arn:aws:sqs:us-east-1:12345:dlq", "Description": "Test function.", "Environment": { "Env1": "Value1" }, "Handler": "handler", "KmsKeyArn": "arn:aws:kms:us-east-1:12345:key", "MemorySize": 128, "Runtime": "python2.7", "Timeout": 3, } self.ctx = Context(config=Config({'namespace': 'test'}))
def setUp(self): self.common_variables = { "ServiceName": "WorkerService", "Image": "fake_repo/image:12345", "Command": ["/bin/run", "--args 1"], "Cluster": "fake-fargate-cluster", "CPU": 1024, "Memory": 2048, "Count": 3, "Environment": { "DATABASE_URL": "sql://fake_db/fake_db", "DEBUG": "false", }, } self.ctx = Context({'namespace': 'test', 'environment': 'test'})
def setUp(self): self.context = Context(config=Config({ 'namespace': 'test', 'stacker_bucket': 'test' })) self.provider = mock_provider(region="us-east-1") self.mock_process = MockProcess() self.popen_mock = \ mock.patch('stacker.hooks.command.Popen', return_value=self.mock_process).start() self.devnull = mock.Mock() self.devnull_mock = \ mock.patch('stacker.hooks.command._devnull', return_value=self.devnull).start()
def test_Raises_StackDoesNotExist_from_lookup_non_included_stack(self): # This test is testing the specific scenario listed in PR 466 # Because the issue only threw a KeyError when a stack was missing # in the `--stacks` flag at runtime of a `stacker build` run # but needed for an output lookup in the stack specified mock_provider = mock.MagicMock() context = Context(config=Config({ "namespace": "namespace", "stacks": [ {"name": "bastion", "variables": {"test": "${output vpc::something}"} }] })) build_action = build.Action(context, provider=mock_provider) with self.assertRaises(StackDoesNotExist): build_action._generate_plan()
def test_hook_with_sys_path(self): config = Config({ "namespace": "test", "sys_path": "stacker/tests", "pre_build": [ { "data_key": "myHook", "path": "fixtures.mock_hooks.mock_hook", "required": True, "args": { "value": "mockResult"}}]}) load(config) context = Context(config=config) stage = "pre_build" handle_hooks(stage, context.config[stage], "mock-region-1", context) self.assertEqual("mockResult", context.hook_data["myHook"]["result"])
def setUp(self): self.context = Context({'namespace': 'namespace'}) self.build_action = build.Action(self.context, provider=TestProvider())
class TestStack(unittest.TestCase): def setUp(self): self.sd = {"name": "test"} self.context = Context({"namespace": "namespace"}) self.stack = Stack( definition=generate_definition("vpc", 1), context=self.context, ) def test_stack_requires(self): definition = generate_definition( "vpc", 1, parameters={ "ExternalParameter": "fakeStack2::FakeParameter", }, requires=[self.context.get_fqn("fakeStack")], ) stack = Stack(definition=definition, context=self.context) self.assertIn( self.context.get_fqn("fakeStack"), stack.requires, ) self.assertIn( self.context.get_fqn("fakeStack2"), stack.requires, ) def test_empty_parameters(self): build_action_parameters = {} self.assertEqual({}, _gather_parameters(self.sd, build_action_parameters)) def test_generic_build_action_override(self): sdef = self.sd sdef["parameters"] = {"Address": "10.0.0.1", "Foo": "BAR"} build_action_parameters = {"Address": "192.168.1.1"} result = _gather_parameters(sdef, build_action_parameters) self.assertEqual(result["Address"], "192.168.1.1") self.assertEqual(result["Foo"], "BAR") def test_stack_specific_override(self): sdef = self.sd sdef["parameters"] = {"Address": "10.0.0.1", "Foo": "BAR"} build_action_parameters = {"test::Address": "192.168.1.1"} result = _gather_parameters(sdef, build_action_parameters) self.assertEqual(result["Address"], "192.168.1.1") self.assertEqual(result["Foo"], "BAR") def test_invalid_stack_specific_override(self): sdef = self.sd sdef["parameters"] = {"Address": "10.0.0.1", "Foo": "BAR"} build_action_parameters = {"FAKE::Address": "192.168.1.1"} result = _gather_parameters(sdef, build_action_parameters) self.assertEqual(result["Address"], "10.0.0.1") self.assertEqual(result["Foo"], "BAR") def test_specific_vs_generic_build_action_override(self): sdef = self.sd sdef["parameters"] = {"Address": "10.0.0.1", "Foo": "BAR"} build_action_parameters = { "test::Address": "192.168.1.1", "Address": "10.0.0.1"} result = _gather_parameters(sdef, build_action_parameters) self.assertEqual(result["Address"], "192.168.1.1") self.assertEqual(result["Foo"], "BAR")
class TestDestroyAction(unittest.TestCase): def setUp(self): self.context = Context({'namespace': 'namespace'}) self.context.config = { 'stacks': [ {'name': 'vpc'}, {'name': 'bastion', 'requires': ['vpc']}, {'name': 'instance', 'requires': ['vpc', 'bastion']}, {'name': 'db', 'requires': ['instance', 'vpc', 'bastion']}, {'name': 'other', 'requires': ['db']}, ], } self.action = destroy.Action(self.context, provider=mock.MagicMock()) def test_generate_plan(self): plan = self.action._generate_plan() self.assertEqual( [self.context.get_fqn(s) for s in ['other', 'db', 'instance', 'bastion', 'vpc']], plan.keys(), ) def test_only_execute_plan_when_forced(self): with mock.patch.object(self.action, '_generate_plan') as mock_generate_plan: self.action.run(force=False) self.assertEqual(mock_generate_plan().execute.call_count, 0) def test_execute_plan_when_forced(self): with mock.patch.object(self.action, '_generate_plan') as mock_generate_plan: self.action.run(force=True) self.assertEqual(mock_generate_plan().execute.call_count, 1) def test_destroy_stack_complete_if_state_submitted(self): # Simulate the provider not being able to find the stack (a result of # it being successfully deleted) self.action.provider = mock.MagicMock() self.action.provider.get_stack.side_effect = StackDoesNotExist('mock') status = self.action._destroy_stack(MockStack('vpc'), status=PENDING) # if we haven't processed the step (ie. has never been SUBMITTED, should be skipped) self.assertEqual(status, SKIPPED) status = self.action._destroy_stack(MockStack('vpc'), status=SUBMITTED) # if we have processed the step and then can't find the stack, it means # we successfully deleted it self.assertEqual(status, COMPLETE) def test_destroy_stack_in_parallel(self): count = {'_count': 0} mock_provider = mock.MagicMock() self.context.config = { 'stacks': [ {'name': 'vpc'}, {'name': 'bastion', 'requires': ['vpc']}, {'name': 'instance', 'requires': ['vpc']}, {'name': 'db', 'requies': ['vpc']}, ], } stacks_dict = self.context.get_stacks_dict() def get_stack(stack_name): return stacks_dict.get(stack_name) def get_stack_staggered(stack_name): count['_count'] += 1 if not count['_count'] % 2: raise StackDoesNotExist(stack_name) return stacks_dict.get(stack_name) def wait_func(*args): # we want "get_stack" above to return staggered results for the stack # being "deleted" to simulate waiting on stacks to complete mock_provider.get_stack.side_effect = get_stack_staggered plan = self.action._generate_plan() plan._wait_func = wait_func # swap for mock provider plan.provider = mock_provider self.action.provider = mock_provider # we want "get_stack" to return the mock stacks above on the first # pass. "wait_func" will simulate the stack being deleted every second # pass mock_provider.get_stack.side_effect = get_stack mock_provider.is_stack_destroyed.return_value = False mock_provider.is_stack_in_progress.return_value = True independent_stacks = filter(lambda x: x.name != 'vpc', self.context.get_stacks()) while not plan._single_run(): # vpc should be the last stack that is deleted if plan['namespace-vpc'].completed: self.assertFalse(plan.list_pending()) # all of the independent stacks should be submitted at the same time if any([plan[stack.fqn].submitted for stack in independent_stacks]): self.assertTrue(all([plan[stack.fqn].submitted for stack in independent_stacks])) wait_func() def test_destroy_stack_step_statuses(self): mock_provider = mock.MagicMock() stacks_dict = self.context.get_stacks_dict() def get_stack(stack_name): return stacks_dict.get(stack_name) plan = self.action._generate_plan() _, step = plan.list_pending()[0] # we need the AWS provider to generate the plan, but swap it for # the mock one to make the test easier self.action.provider = mock_provider # simulate stack doesn't exist and we haven't submitted anything for deletion mock_provider.get_stack.side_effect = StackDoesNotExist('mock') status = step.run() self.assertEqual(status, SKIPPED) # simulate stack getting successfully deleted mock_provider.get_stack.side_effect = get_stack mock_provider.is_stack_destroyed.return_value = False mock_provider.is_stack_in_progress.return_value = False status = step.run() self.assertEqual(status, SUBMITTED) mock_provider.is_stack_destroyed.return_value = False mock_provider.is_stack_in_progress.return_value = True status = step.run() self.assertEqual(status, SUBMITTED) mock_provider.is_stack_destroyed.return_value = True mock_provider.is_stack_in_progress.return_value = False status = step.run() self.assertEqual(status, COMPLETE)
def test_context_get_fqn_empty_namespace(self): context = Context(config=Config({"namespace": ""})) fqn = context.get_fqn("vpc") self.assertEqual(fqn, "vpc") self.assertEqual(context.tags, {})
def setUp(self): self.ctx = Context({"namespace": "test-ns"}) self.ctx.set_hook_data("fake_hook", {"result": "good"})
def test_context_namespace_delimiter_is_overriden_and_is_empty(self): config = Config({"namespace": "namespace", "namespace_delimiter": ""}) context = Context(config=config) fqn = context.get_fqn("stack1") self.assertEqual(fqn, "namespacestack1")
class TestBuildAction(unittest.TestCase): def setUp(self): self.context = Context({'namespace': 'namespace'}) self.build_action = build.Action(self.context, provider=TestProvider()) def _get_context(self, **kwargs): config = {'stacks': [ {'name': 'vpc'}, {'name': 'bastion', 'parameters': {'test': 'vpc::something'}}, {'name': 'db', 'parameters': {'test': 'vpc::something', 'else': 'bastion::something'}}, {'name': 'other', 'parameters': {}} ]} return Context({'namespace': 'namespace'}, config=config, **kwargs) def test_resolve_parameters_referencing_non_existant_output(self): parameters = { 'param_1': 'mock::output_1', 'param_2': 'other::does_not_exist', } self.build_action.provider.set_outputs({ self.context.get_fqn('mock'): {'output_1': 'output'}, self.context.get_fqn('other'): {}, }) mock_blueprint = mock.MagicMock() type(mock_blueprint).parameters = parameters with self.assertRaises(exceptions.OutputDoesNotExist): self.build_action._resolve_parameters(parameters, mock_blueprint) def test_resolve_parameters(self): parameters = { 'param_1': 'mock::output_1', 'param_2': 'other::output_2', } self.build_action.provider.set_outputs({ self.context.get_fqn('mock'): {'output_1': 'output1'}, self.context.get_fqn('other'): {'output_2': 'output2'}, }) mock_blueprint = mock.MagicMock() type(mock_blueprint).parameters = parameters resolved_parameters = self.build_action._resolve_parameters( parameters, mock_blueprint, ) self.assertEqual(resolved_parameters['param_1'], 'output1') self.assertEqual(resolved_parameters['param_2'], 'output2') def test_resolve_parameters_referencing_non_existant_stack(self): parameters = { 'param_1': 'mock::output_1', } self.build_action.provider.set_outputs({}) mock_blueprint = mock.MagicMock() type(mock_blueprint).parameters = parameters with self.assertRaises(exceptions.StackDoesNotExist): self.build_action._resolve_parameters(parameters, mock_blueprint) def test_gather_missing_from_stack(self): stack_params = {'Address': '10.0.0.1'} stack = MockStack(stack_params) def_params = {} required = ['Address'] self.assertEqual( self.build_action._handle_missing_parameters(def_params, required, stack), stack_params.items()) def test_missing_params_no_stack(self): params = {} required = ['Address'] with self.assertRaises(exceptions.MissingParameterException) as cm: self.build_action._handle_missing_parameters(params, required) self.assertEqual(cm.exception.parameters, required) def test_stack_params_dont_override_given_params(self): stack_params = {'Address': '10.0.0.1'} stack = MockStack(stack_params) def_params = {'Address': '192.168.0.1'} required = ['Address'] result = self.build_action._handle_missing_parameters(def_params, required, stack) self.assertEqual(result, def_params.items()) def test_get_dependencies(self): context = self._get_context() build_action = build.Action(context) dependencies = build_action._get_dependencies() self.assertEqual( dependencies[context.get_fqn('bastion')], set([context.get_fqn('vpc')]), ) self.assertEqual( dependencies[context.get_fqn('db')], set([context.get_fqn(s) for s in['vpc', 'bastion']]), ) self.assertFalse(dependencies[context.get_fqn('other')]) def test_get_stack_execution_order(self): context = self._get_context() build_action = build.Action(context) dependencies = build_action._get_dependencies() execution_order = build_action.get_stack_execution_order(dependencies) self.assertEqual( execution_order, [context.get_fqn(s) for s in ['other', 'vpc', 'bastion', 'db']], ) def test_generate_plan(self): context = self._get_context() build_action = build.Action(context) plan = build_action._generate_plan() self.assertEqual( plan.keys(), [context.get_fqn(s) for s in ['other', 'vpc', 'bastion', 'db']], ) def test_dont_execute_plan_when_outline_specified(self): context = self._get_context() build_action = build.Action(context) with mock.patch.object(build_action, '_generate_plan') as \ mock_generate_plan: build_action.run(outline=True) self.assertEqual(mock_generate_plan().execute.call_count, 0) def test_execute_plan_when_outline_not_specified(self): context = self._get_context() build_action = build.Action(context) with mock.patch.object(build_action, '_generate_plan') as \ mock_generate_plan: build_action.run(outline=False) self.assertEqual(mock_generate_plan().execute.call_count, 1) def test_launch_stack_step_statuses(self): mock_provider = mock.MagicMock() mock_stack = mock.MagicMock() context = self._get_context() build_action = build.Action(context, provider=mock_provider) plan = build_action._generate_plan() _, step = plan.list_pending()[0] step.stack = mock.MagicMock() step.stack.locked = False # mock provider shouldn't return a stack at first since it hasn't been # launched mock_provider.get_stack.return_value = None with mock.patch.object(build_action, 's3_stack_push'): # initial run should return SUBMITTED since we've passed off to CF status = step.run() self.assertEqual(status, SUBMITTED) # provider should now return the CF stack since it exists mock_provider.get_stack.return_value = mock_stack # simulate that we're still in progress mock_provider.is_stack_in_progress.return_value = True mock_provider.is_stack_completed.return_value = False status = step.run() step.set_status(status) # status should still be SUBMITTED since we're waiting for it to # complete self.assertEqual(status, SUBMITTED) # simulate completed stack mock_provider.is_stack_completed.return_value = True mock_provider.is_stack_in_progress.return_value = False status = step.run() self.assertEqual(status, COMPLETE) # simulate stack should be skipped mock_provider.is_stack_completed.return_value = False mock_provider.is_stack_in_progress.return_value = False mock_provider.update_stack.side_effect = StackDidNotChange status = step.run() self.assertEqual(status, SKIPPED) # simulate an update is required mock_provider.reset_mock() mock_provider.update_stack.side_effect = None step.set_status(PENDING) status = step.run() self.assertEqual(status, SUBMITTED) self.assertEqual(mock_provider.update_stack.call_count, 1) def test_should_update(self): test_scenario = namedtuple('test_scenario', ['locked', 'force', 'result']) test_scenarios = ( test_scenario(locked=False, force=False, result=True), test_scenario(locked=False, force=True, result=True), test_scenario(locked=True, force=False, result=False), test_scenario(locked=True, force=True, result=True) ) mock_stack = mock.MagicMock(["locked", "force", "name"]) mock_stack.name = "test-stack" for t in test_scenarios: mock_stack.locked = t.locked mock_stack.force = t.force self.assertEqual(build.should_update(mock_stack), t.result) def test_should_submit(self): test_scenario = namedtuple('test_scenario', ['enabled', 'result']) test_scenarios = ( test_scenario(enabled=False, result=False), test_scenario(enabled=True, result=True), ) mock_stack = mock.MagicMock(["enabled", "name"]) mock_stack.name = "test-stack" for t in test_scenarios: mock_stack.enabled = t.enabled self.assertEqual(build.should_submit(mock_stack), t.result)
def test_context_get_stacks(self): context = Context(config=self.config) self.assertEqual(len(context.get_stacks()), 2)
def test_context_get_stacks_dict_use_fqn(self): context = Context(config=self.config) stacks_dict = context.get_stacks_dict() stack_names = sorted(stacks_dict.keys()) self.assertEqual(stack_names[0], "namespace-stack1") self.assertEqual(stack_names[1], "namespace-stack2")
def test_context_get_fqn(self): context = Context(config=self.config) fqn = context.get_fqn() self.assertEqual(fqn, "namespace")
def test_context_get_fqn_stack_name(self): context = Context(config=self.config) fqn = context.get_fqn("stack1") self.assertEqual(fqn, "namespace-stack1")
def test_context_get_fqn_replace_dot(self): context = Context(config=Config({"namespace": "my.namespace"})) fqn = context.get_fqn() self.assertEqual(fqn, "my-namespace")
class TestDestroyAction(unittest.TestCase): def setUp(self): config = Config({ "namespace": "namespace", "stacks": [ {"name": "vpc"}, {"name": "bastion", "requires": ["vpc"]}, {"name": "instance", "requires": ["vpc", "bastion"]}, {"name": "db", "requires": ["instance", "vpc", "bastion"]}, {"name": "other", "requires": ["db"]}, ], }) self.context = Context(config=config) self.action = destroy.Action(self.context, cancel=MockThreadingEvent()) def test_generate_plan(self): plan = self.action._generate_plan() self.assertEqual( { 'vpc': set( ['db', 'instance', 'bastion']), 'other': set([]), 'bastion': set( ['instance', 'db']), 'instance': set( ['db']), 'db': set( ['other'])}, plan.graph.to_dict() ) def test_only_execute_plan_when_forced(self): with mock.patch.object(self.action, "_generate_plan") as \ mock_generate_plan: self.action.run(force=False) self.assertEqual(mock_generate_plan().execute.call_count, 0) def test_execute_plan_when_forced(self): with mock.patch.object(self.action, "_generate_plan") as \ mock_generate_plan: self.action.run(force=True) self.assertEqual(mock_generate_plan().execute.call_count, 1) def test_destroy_stack_complete_if_state_submitted(self): # Simulate the provider not being able to find the stack (a result of # it being successfully deleted) provider = mock.MagicMock() provider.get_stack.side_effect = StackDoesNotExist("mock") self.action.provider_builder = MockProviderBuilder(provider) status = self.action._destroy_stack(MockStack("vpc"), status=PENDING) # if we haven't processed the step (ie. has never been SUBMITTED, # should be skipped) self.assertEqual(status, SKIPPED) status = self.action._destroy_stack(MockStack("vpc"), status=SUBMITTED) # if we have processed the step and then can't find the stack, it means # we successfully deleted it self.assertEqual(status, COMPLETE) def test_destroy_stack_step_statuses(self): mock_provider = mock.MagicMock() stacks_dict = self.context.get_stacks_dict() def get_stack(stack_name): return stacks_dict.get(stack_name) plan = self.action._generate_plan() step = plan.steps[0] # we need the AWS provider to generate the plan, but swap it for # the mock one to make the test easier self.action.provider_builder = MockProviderBuilder(mock_provider) # simulate stack doesn't exist and we haven't submitted anything for # deletion mock_provider.get_stack.side_effect = StackDoesNotExist("mock") step.run() self.assertEqual(step.status, SKIPPED) # simulate stack getting successfully deleted mock_provider.get_stack.side_effect = get_stack mock_provider.is_stack_destroyed.return_value = False mock_provider.is_stack_in_progress.return_value = False step._run_once() self.assertEqual(step.status, SUBMITTED) mock_provider.is_stack_destroyed.return_value = False mock_provider.is_stack_in_progress.return_value = True step._run_once() self.assertEqual(step.status, SUBMITTED) mock_provider.is_stack_destroyed.return_value = True mock_provider.is_stack_in_progress.return_value = False step._run_once() self.assertEqual(step.status, COMPLETE)