Example #1
0
    def test_stack_requires_when_locked(self):
        definition = generate_definition(
            base_name="vpc",
            stack_id=1,
            variables={
                "Var1":
                "${noop fakeStack3::FakeOutput}",
                "Var2":
                ("some.template.value:${output fakeStack2::FakeOutput}:"
                 "${output fakeStack::FakeOutput}"),
                "Var3":
                "${output fakeStack::FakeOutput},"
                "${output fakeStack2::FakeOutput}",
            },
            requires=["fakeStack"],
        )
        stack = Stack(definition=definition, context=self.context)

        stack.locked = True
        self.assertEqual(len(stack.requires), 0)

        # TODO(ejholmes): When the stack is in `--force`, it's not really
        # locked. Maybe it would be better if `stack.locked` were false when
        # the stack is in `--force`.
        stack.force = True
        self.assertEqual(len(stack.requires), 2)
Example #2
0
    def test_execute_plan_exception(self):
        vpc = Stack(definition=generate_definition('vpc', 1),
                    context=self.context)
        bastion = Stack(definition=generate_definition('bastion',
                                                       1,
                                                       requires=[vpc.name]),
                        context=self.context)

        calls = []

        def fn(stack, status=None):
            calls.append(stack.fqn)
            if stack.name == vpc_step.name:
                raise ValueError('Boom')
            return COMPLETE

        vpc_step = Step(vpc, fn)
        bastion_step = Step(bastion, fn)
        plan = build_plan(description="Test", steps=[vpc_step, bastion_step])

        with self.assertRaises(PlanFailed):
            plan.execute(walk)

        self.assertEquals(calls, ['namespace-vpc.1'])
        self.assertEquals(vpc_step.status, FAILED)
Example #3
0
    def test_execute_plan_filtered(self):
        vpc = Stack(definition=generate_definition('vpc', 1),
                    context=self.context)
        db = Stack(definition=generate_definition('db', 1,
                                                  requires=[vpc.name]),
                   context=self.context)
        app = Stack(definition=generate_definition('app',
                                                   1,
                                                   requires=[db.name]),
                    context=self.context)

        calls = []

        def fn(stack, status=None):
            calls.append(stack.fqn)
            return COMPLETE

        plan = build_plan(description="Test",
                          steps=[Step(vpc, fn),
                                 Step(db, fn),
                                 Step(app, fn)],
                          targets=['db.1'])
        plan.execute(walk)

        self.assertEquals(calls, ['namespace-vpc.1', 'namespace-db.1'])
Example #4
0
    def test_execute_plan_failed(self):
        vpc = Stack(definition=generate_definition('vpc', 1),
                    context=self.context)
        bastion = Stack(definition=generate_definition('bastion',
                                                       1,
                                                       requires=[vpc.name]),
                        context=self.context)
        db = Stack(definition=generate_definition('db', 1),
                   context=self.context)

        calls = []

        def fn(stack, status=None):
            calls.append(stack.fqn)
            if stack.name == vpc_step.name:
                return FAILED
            return COMPLETE

        vpc_step = Step(vpc, fn)
        bastion_step = Step(bastion, fn)
        db_step = Step(db, fn)

        plan = build_plan(description="Test",
                          steps=[vpc_step, bastion_step, db_step])
        with self.assertRaises(PlanFailed):
            plan.execute(walk)

        calls.sort()

        self.assertEquals(calls, ['namespace-db.1', 'namespace-vpc.1'])
Example #5
0
    def test_stack_requires_when_locked(self):
        definition = generate_definition(
            base_name="vpc",
            stack_id=1,
            variables={
                "Var1": "${noop fakeStack3::FakeOutput}",
                "Var2": (
                    "some.template.value:${output fakeStack2::FakeOutput}:"
                    "${output fakeStack::FakeOutput}"
                ),
                "Var3": "${output fakeStack::FakeOutput},"
                        "${output fakeStack2::FakeOutput}",
            },
            requires=["fakeStack"],
        )
        stack = Stack(definition=definition, context=self.context)

        stack.locked = True
        self.assertEqual(len(stack.requires), 0)

        # TODO(ejholmes): When the stack is in `--force`, it's not really
        # locked. Maybe it would be better if `stack.locked` were false when
        # the stack is in `--force`.
        stack.force = True
        self.assertEqual(len(stack.requires), 2)
Example #6
0
    def test_variable_resolve_nested_lookup(self):
        stack = Stack(definition=generate_definition("vpc", 1),
                      context=self.context)
        stack.set_outputs({
            "FakeOutput": "resolved",
            "FakeOutput2": "resolved2",
        })

        def mock_handler(value, context, provider, **kwargs):
            return "looked up: {}".format(value)

        register_lookup_handler("lookup", mock_handler)
        self.context.get_stack.return_value = stack
        var = Variable(
            "Param1",
            "${lookup ${lookup ${output fakeStack::FakeOutput}}}",
        )
        self.assertEqual(
            len(var.lookups),
            1,
            "should only parse out the first complete lookup first",
        )
        var.resolve(self.context, self.provider)
        self.assertTrue(var.resolved)
        self.assertEqual(var.value, "looked up: looked up: resolved")
Example #7
0
 def test_stack_should_submit(self):
     for enabled in (True, False):
         definition = generate_definition(
             base_name="vpc",
             stack_id=1,
             enabled=enabled)
         stack = Stack(definition=definition, context=self.context)
         self.assertEqual(stack.should_submit(), enabled)
Example #8
0
 def test_output_handler(self):
     stack = Stack(definition=generate_definition("vpc", 1),
                   context=self.context)
     stack.set_outputs({"SomeOutput": "Test Output"})
     self.context.get_stack.return_value = stack
     value = handler("stack-name::SomeOutput", context=self.context)
     self.assertEqual(value, "Test Output")
     self.assertEqual(self.context.get_stack.call_count, 1)
     args = self.context.get_stack.call_args
     self.assertEqual(args[0][0], "stack-name")
Example #9
0
    def test_execute_plan_ensure_parallel_builds(self):
        # key: stack_name, value: current iteration
        work_states = {}
        submitted_state = 0
        # It takes 4 iterations for each task to finish
        finished_state = 3

        def _run_func(stack, *args, **kwargs):
            if stack.name not in work_states:
                work_states[stack.name] = submitted_state
                return SUBMITTED

            if work_states[stack.name] == finished_state:
                return COMPLETE

            work_states[stack.name] += 1
            return SUBMITTED

        vpc_stack = Stack(definition=generate_definition("vpc", 1),
                          context=self.context)
        web_stack = Stack(
            definition=generate_definition("web", 2, requires=[vpc_stack.fqn]),
            context=self.context,
        )
        db_stack = Stack(
            definition=generate_definition("db", 3, requires=[vpc_stack.fqn]),
            context=self.context,
        )

        plan = Plan(description="Test", sleep_time=0)
        for stack in [vpc_stack, web_stack, db_stack]:
            plan.add(
                stack=stack,
                run_func=_run_func,
                requires=stack.requires,
            )

        parallel_success = False
        while not plan._single_run():
            vpc_step = plan[vpc_stack.fqn]
            web_step = plan[web_stack.fqn]
            db_step = plan[db_stack.fqn]
            if not vpc_step.completed:
                self.assertFalse(web_step.submitted)
                self.assertFalse(db_step.submitted)
            else:
                # If the vpc step is complete, and we see both the web & db
                # steps submitted during the same run, then parallel running
                # works
                if web_step.status == SUBMITTED and \
                        db_step.status == SUBMITTED:
                    parallel_success = True
        self.assertTrue(parallel_success)
Example #10
0
    def test_plan(self):
        vpc = Stack(
            definition=generate_definition('vpc', 1),
            context=self.context)
        bastion = Stack(
            definition=generate_definition('bastion', 1, requires=[vpc.fqn]),
            context=self.context)

        plan = build_plan(description="Test", steps=[
            Step(vpc, fn=None), Step(bastion, fn=None)])

        self.assertEqual(plan.graph.to_dict(), {
            'namespace-bastion.1': set(['namespace-vpc.1']),
            'namespace-vpc.1': set([])})
Example #11
0
    def test_variable_resolve_simple_lookup(self):
        stack = Stack(
            definition=generate_definition("vpc", 1),
            context=self.context)
        stack.set_outputs({
            "FakeOutput": "resolved",
            "FakeOutput2": "resolved2",
        })

        self.context.get_stack.return_value = stack

        var = Variable("Param1", "${output fakeStack::FakeOutput}")
        var.resolve(self.context, self.provider)
        self.assertTrue(var.resolved)
        self.assertEqual(var.value, "resolved")
Example #12
0
    def test_build_graph_cyclic_dependencies(self):
        vpc = Stack(definition=generate_definition('vpc', 1),
                    context=self.context)
        db = Stack(definition=generate_definition('db', 1, requires=['app.1']),
                   context=self.context)
        app = Stack(definition=generate_definition('app', 1,
                                                   requires=['db.1']),
                    context=self.context)

        with self.assertRaises(GraphError) as expected:
            build_graph([Step(vpc, None), Step(db, None), Step(app, None)])
        message = ("Error detected when adding 'db.1' "
                   "as a dependency of 'app.1': graph is "
                   "not acyclic")
        self.assertEqual(str(expected.exception), message)
Example #13
0
    def test_plan(self):
        vpc = Stack(
            definition=generate_definition('vpc', 1),
            context=self.context)
        bastion = Stack(
            definition=generate_definition('bastion', 1, requires=[vpc.name]),
            context=self.context)

        graph = build_graph([
            Step(vpc, fn=None), Step(bastion, fn=None)])
        plan = build_plan(description="Test", graph=graph)

        self.assertEqual(plan.graph.to_dict(), {
            'bastion.1': set(['vpc.1']),
            'vpc.1': set([])})
Example #14
0
    def test_execute_plan_with_watchers(self, patched_multiprocessing):
        watch_func = mock.MagicMock()
        plan = Plan(description="Test", sleep_time=0, watch_func=watch_func)
        previous_stack = None
        for i in range(5):
            overrides = {}
            if previous_stack:
                overrides["requires"] = [previous_stack.fqn]
            stack = Stack(
                definition=generate_definition("vpc", i, **overrides),
                context=self.context,
            )
            previous_stack = stack
            plan.add(
                stack=stack,
                run_func=self._run_func,
                requires=stack.requires,
            )

        plan.execute()
        self.assertEqual(self.count, 9)
        self.assertEqual(len(plan.list_skipped()), 1)
        self.assertEqual(patched_multiprocessing.Process().start.call_count, 5)
        # verify we terminate the process when the stack is finished and also
        # redundantly terminate the process after execution
        self.assertEqual(
            patched_multiprocessing.Process().terminate.call_count, 10)
Example #15
0
    def test_dump_no_provider_lookups(self, *args):
        plan = Plan(description="Test", sleep_time=0)
        previous_stack = None
        for i in range(5):
            overrides = {
                "variables": {
                    "Var1": "${output fakeStack::FakeOutput}",
                    "Var2": "${xref fakeStack::FakeOutput2}",
                },
            }
            if previous_stack:
                overrides["requires"] = [previous_stack.fqn]
            stack = Stack(
                definition=generate_definition("vpc", i, **overrides),
                context=self.context,
            )
            previous_stack = stack
            plan.add(
                stack=stack,
                run_func=self._run_func,
                requires=stack.requires,
            )

        with self.assertRaises(FailedVariableLookup):
            plan.dump("test", context=self.context)
Example #16
0
 def test_stack_cfn_parameters(self):
     definition = generate_definition(
         base_name="vpc",
         stack_id=1,
         variables={
             "Param1": "${output fakeStack::FakeOutput}",
         },
     )
     stack = Stack(definition=definition, context=self.context)
     stack._blueprint = MagicMock()
     stack._blueprint.get_parameter_values.return_value = {
         "Param2": "Some Resolved Value",
     }
     self.assertEqual(len(stack.parameter_values), 1)
     param = stack.parameter_values["Param2"]
     self.assertEqual(param, "Some Resolved Value")
Example #17
0
    def test_dump(self, *args):
        requires = None
        steps = []

        for i in range(5):
            overrides = {
                "variables": {
                    "PublicSubnets": "1",
                    "SshKeyName": "1",
                    "PrivateSubnets": "1",
                    "Random": "${noop something}",
                },
                "requires": requires,
            }

            stack = Stack(definition=generate_definition(
                'vpc', i, **overrides),
                          context=self.context)
            requires = [stack.name]

            steps += [Step(stack, None)]

        plan = build_plan(description="Test", steps=steps)

        tmp_dir = tempfile.mkdtemp()
        try:
            plan.dump(tmp_dir, context=self.context)

            for step in plan.steps:
                template_path = os.path.join(
                    tmp_dir, stack_template_key_name(step.stack.blueprint))
                self.assertTrue(os.path.isfile(template_path))
        finally:
            shutil.rmtree(tmp_dir)
Example #18
0
 def test_stack_requires(self):
     definition = generate_definition(
         base_name="vpc",
         stack_id=1,
         variables={
             "Var1":
             "${noop fakeStack3::FakeOutput}",
             "Var2":
             ("some.template.value:${output fakeStack2::FakeOutput}:"
              "${output fakeStack::FakeOutput}"),
             "Var3":
             "${output fakeStack::FakeOutput},"
             "${output fakeStack2::FakeOutput}",
         },
         requires=["fakeStack"],
     )
     stack = Stack(definition=definition, context=self.context)
     self.assertEqual(len(stack.requires), 2)
     self.assertIn(
         "fakeStack",
         stack.requires,
     )
     self.assertIn(
         "fakeStack2",
         stack.requires,
     )
Example #19
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 #20
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 #21
0
 def test_stack_cfn_parameters(self):
     definition = generate_definition(
         base_name="vpc",
         stack_id=1,
         variables={
             "Param1": "${output fakeStack::FakeOutput}",
         },
     )
     stack = Stack(definition=definition, context=self.context)
     stack._blueprint = MagicMock()
     stack._blueprint.get_parameter_values.return_value = {
         "Param2": "Some Resolved Value",
     }
     self.assertEqual(len(stack.parameter_values), 1)
     param = stack.parameter_values["Param2"]
     self.assertEqual(param, "Some Resolved Value")
Example #22
0
    def test_stack_should_update(self):
        test_scenarios = [
            dict(locked=False, force=False, result=True),
            dict(locked=False, force=True, result=True),
            dict(locked=True, force=False, result=False),
            dict(locked=True, force=True, result=True)
        ]

        for t in test_scenarios:
            definition = generate_definition(
                base_name="vpc",
                stack_id=1,
                locked=t['locked'])
            stack = Stack(definition=definition, context=self.context,
                          force=t['force'])
            self.assertEqual(stack.should_update(), t['result'])
Example #23
0
 def test_stack_tags_extra(self):
     self.config.tags = {"environment": "prod"}
     definition = generate_definition(base_name="vpc",
                                      stack_id=1,
                                      tags={"app": "graph"})
     stack = Stack(definition=definition, context=self.context)
     self.assertEquals(stack.tags, {"environment": "prod", "app": "graph"})
Example #24
0
    def test_reset_after_dump(self, *args):
        plan = Plan(description="Test", sleep_time=0)
        previous_stack = None
        for i in range(5):
            overrides = {
                "variables": {
                    "PublicSubnets": "1",
                    "SshKeyName": "1",
                    "PrivateSubnets": "1",
                    "Random": "${noop something}",
                },
            }
            if previous_stack:
                overrides["requires"] = [previous_stack.fqn]
            stack = Stack(
                definition=generate_definition("vpc", i, **overrides),
                context=self.context,
            )
            previous_stack = stack
            plan.add(
                stack=stack,
                run_func=self._run_func,
                requires=stack.requires,
            )

        plan.dump("test", context=self.context)
        self.assertEqual(len(plan.list_pending()), len(plan))
Example #25
0
 def test_plan_steps_listed_with_fqn(self):
     plan = Plan(description="Test", sleep_time=0)
     stack = Stack(definition=generate_definition("vpc", 1),
                   context=self.context)
     plan.add(stack=stack, run_func=lambda x, y: (x, y))
     steps = plan.list_pending()
     self.assertEqual(steps[0][0], stack.fqn)
Example #26
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 #27
0
    def test_execute_plan(self):
        vpc = Stack(
            definition=generate_definition('vpc', 1),
            context=self.context)
        bastion = Stack(
            definition=generate_definition('bastion', 1, requires=[vpc.fqn]),
            context=self.context)

        calls = []

        def fn(stack, status=None):
            calls.append(stack.fqn)
            return COMPLETE

        plan = build_plan(
            description="Test", steps=[Step(vpc, fn), Step(bastion, fn)])
        plan.execute()

        self.assertEquals(calls, ['namespace-vpc.1', 'namespace-bastion.1'])
Example #28
0
 def test_step_must_return_status(self):
     plan = Plan(description="Test", sleep_time=0)
     stack = Stack(definition=generate_definition("vpc", 1),
                   context=mock.MagicMock())
     plan.add(
         stack=stack,
         run_func=lambda x, **kwargs: (x),
     )
     with self.assertRaises(ValueError):
         plan.execute()
Example #29
0
    def test_variable_resolve_multiple_lookups_string(self):
        var = Variable(
            "Param1",
            "url://${output fakeStack::FakeOutput}@"
            "${output fakeStack::FakeOutput2}",
        )

        stack = Stack(
            definition=generate_definition("vpc", 1),
            context=self.context)
        stack.set_outputs({
            "FakeOutput": "resolved",
            "FakeOutput2": "resolved2",
        })

        self.context.get_stack.return_value = stack
        var.resolve(self.context, self.provider)
        self.assertTrue(var.resolved)
        self.assertEqual(var.value, "url://resolved@resolved2")
Example #30
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 #31
0
    def test_build_plan_missing_dependency(self):
        bastion = Stack(definition=generate_definition(
            'bastion', 1, requires=['namespace-vpc.1']),
                        context=self.context)

        with self.assertRaises(GraphError) as expected:
            build_plan(description="Test", steps=[Step(bastion, None)])
        message = ("Error detected when adding 'namespace-vpc.1' "
                   "as a dependency of 'namespace-bastion.1': dependent node "
                   "namespace-vpc.1 does not exist")
        self.assertEqual(expected.exception.message, message)
Example #32
0
 def test_stack_requires_circular_ref(self):
     definition = generate_definition(
         base_name="vpc",
         stack_id=1,
         variables={
             "Var1": "${output vpc.1::FakeOutput}",
         },
     )
     stack = Stack(definition=definition, context=self.context)
     with self.assertRaises(ValueError):
         stack.requires
Example #33
0
    def test_variable_resolve_nested_lookup(self):
        stack = Stack(
            definition=generate_definition("vpc", 1),
            context=self.context)
        stack.set_outputs({
            "FakeOutput": "resolved",
            "FakeOutput2": "resolved2",
        })

        def mock_handler(value, context, provider, **kwargs):
            return "looked up: {}".format(value)

        register_lookup_handler("lookup", mock_handler)
        self.context.get_stack.return_value = stack
        var = Variable(
            "Param1",
            "${lookup ${lookup ${output fakeStack::FakeOutput}}}",
        )
        var.resolve(self.context, self.provider)
        self.assertTrue(var.resolved)
        self.assertEqual(var.value, "looked up: looked up: resolved")