Example #1
0
 def setUp(self):
     self.sd = {"name": "test"}
     self.context = Context({"namespace": "namespace"})
     self.stack = Stack(
         definition=generate_definition("vpc", 1),
         context=self.context,
     )
Example #2
0
 def setUp(self):
     self.sd = {"name": "test"}
     self.context = Context({'namespace': 'namespace'})
     self.stack = Stack(
         definition=generate_definition('vpc', 1),
         context=self.context,
     )
Example #3
0
 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())
Example #4
0
 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())
Example #5
0
 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())
Example #6
0
    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")
Example #7
0
    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)
Example #8
0
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)
Example #9
0
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)')
Example #10
0
    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)
Example #11
0
 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)
Example #12
0
 def setUp(self):
     self.sd = {"name": "test"}
     self.context = Context({"namespace": "namespace"})
     self.stack = Stack(
         definition=generate_definition("vpc", 1),
         context=self.context,
     )
Example #13
0
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
Example #14
0
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)
Example #15
0
 def setUp(self):
     self.sd = {"name": "test"}
     self.context = Context('namespace')
     self.stack = Stack(
         definition=generate_definition('vpc', 1),
         context=self.context,
     )
Example #16
0
def mock_context(namespace=None, **kwargs):
    config = Config({"namespace": namespace})
    environment = kwargs.get("environment", {})
    return Context(
        config=config,
        environment=environment,
        **kwargs)
Example #17
0
 def test_context_optional_keys_set(self):
     context = Context(
         config=Config({}),
         stack_names=["stack"],
     )
     self.assertEqual(context.mappings, {})
     self.assertEqual(context.stack_names, ["stack"])
Example #18
0
 def setUp(self):
     self.provider = MagicMock()
     self.context = Context(
         environment={
             'namespace': 'test',
             'env_var': 'val_in_env'}
     )
Example #19
0
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())
Example #20
0
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)
Example #21
0
 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",
     }
Example #22
0
    def setUp(self):
        self.common_variables = [
            Variable("Instances",
                     {"MyInstance": {
                         "ImageId": "ami-abc12345",
                     }})
        ]

        self.ctx = Context({'namespace': 'test', 'environment': 'test'})
Example #23
0
 def setUp(self):
     self.ctx = Context(config=Config({'namespace': 'test'}))
     self.common_variables = {
         "VPC": {
             VPC_NAME: {
                 "CidrBlock": "10.0.0.0/16"
             }
         }
     }
Example #24
0
 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")
Example #25
0
 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)
Example #26
0
    def setUp(self):
        self.common_variables = [
            Variable(
                "SecurityGroups",
                {"MySG1": {
                    "GroupDescription": "My first SecurityGroup",
                }})
        ]

        self.ctx = Context({'namespace': 'test', 'environment': 'test'})
Example #27
0
 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),
     )
Example #28
0
 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())
Example #29
0
 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())
Example #30
0
 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)
Example #31
0
 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'}))
Example #32
0
    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'})
Example #33
0
    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()
Example #34
0
 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()
Example #35
0
 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"])
Example #36
0
 def setUp(self):
     self.context = Context({'namespace': 'namespace'})
     self.build_action = build.Action(self.context, provider=TestProvider())
Example #37
0
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")
Example #38
0
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)
Example #39
0
 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, {})
Example #40
0
 def setUp(self):
     self.ctx = Context({"namespace": "test-ns"})
     self.ctx.set_hook_data("fake_hook", {"result": "good"})
Example #41
0
 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")
Example #42
0
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)
Example #43
0
 def test_context_get_stacks(self):
     context = Context(config=self.config)
     self.assertEqual(len(context.get_stacks()), 2)
Example #44
0
 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")
Example #45
0
 def test_context_get_fqn(self):
     context = Context(config=self.config)
     fqn = context.get_fqn()
     self.assertEqual(fqn, "namespace")
Example #46
0
 def test_context_get_fqn_stack_name(self):
     context = Context(config=self.config)
     fqn = context.get_fqn("stack1")
     self.assertEqual(fqn, "namespace-stack1")
Example #47
0
 def test_context_get_fqn_replace_dot(self):
     context = Context(config=Config({"namespace": "my.namespace"}))
     fqn = context.get_fqn()
     self.assertEqual(fqn, "my-namespace")
Example #48
0
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)