Example #1
0
    def test_execute_plan_no_persist(self) -> None:
        """Test execute plan with no persistent graph."""
        context = CfnginContext(config=self.config)
        context.put_persistent_graph = mock.MagicMock()
        vpc = Stack(definition=generate_definition("vpc", 1), context=context)
        bastion = Stack(
            definition=generate_definition("bastion", 1, requires=[vpc.name]),
            context=context,
        )

        calls: List[str] = []

        def _launch_stack(stack: Stack, status: Optional[Status] = None) -> Status:
            calls.append(stack.fqn)
            return COMPLETE

        graph = Graph.from_steps(
            [Step(vpc, fn=_launch_stack), Step(bastion, fn=_launch_stack)]
        )
        plan = Plan(description="Test", graph=graph, context=context)

        plan.execute(walk)

        self.assertEqual(calls, ["namespace-vpc.1", "namespace-bastion.1"])
        context.put_persistent_graph.assert_not_called()
Example #2
0
    def test_execute_plan_graph_locked(self) -> None:
        """Test execute plan with locked persistent graph."""
        context = CfnginContext(config=self.config)
        context._persistent_graph = Graph.from_dict({"stack1": []}, context)
        context._persistent_graph_lock_code = "1111"
        plan = Plan(description="Test", graph=Graph(), context=context)
        print(plan.locked)

        with self.assertRaises(PersistentGraphLocked):
            plan.execute()
Example #3
0
def mock_context(
    namespace: str = "default",
    extra_config_args: Optional[Dict[str, Any]] = None,
    **kwargs: Any,
) -> CfnginContext:
    """Mock context."""
    config_args = {"namespace": namespace}
    if extra_config_args:
        config_args.update(extra_config_args)
    config = CfnginConfig.parse_obj(config_args)
    if kwargs.get("environment"):
        return CfnginContext(config=config, **kwargs)
    return CfnginContext(config=config, environment={}, **kwargs)
Example #4
0
    def test_plan_targeted(self) -> None:
        """Test plan targeted."""
        context = CfnginContext(config=self.config)
        vpc = Stack(definition=generate_definition("vpc", 1), context=context)
        bastion = Stack(
            definition=generate_definition("bastion", 1, requires=[vpc.name]),
            context=context,
        )
        context.stack_names = [vpc.name]
        graph = Graph.from_steps([Step(vpc, fn=None), Step(bastion, fn=None)])
        plan = Plan(description="Test", graph=graph, context=context)

        self.assertEqual({vpc.name: set()}, plan.graph.to_dict())
Example #5
0
 def setUp(self) -> None:
     """Run before tests."""
     self.provider = MagicMock()
     self.context = CfnginContext(parameters={
         "namespace": "test",
         "env_var": "val_in_env"
     })
Example #6
0
 def _get_context(
     self, extra_config_args: Optional[Dict[str, Any]] = None, **kwargs: Any
 ) -> CfnginContext:
     """Get context."""
     config = {
         "namespace": "namespace",
         "stacks": [
             {"name": "vpc", "template_path": "."},
             {"name": "bastion", "requires": ["vpc"], "template_path": "."},
             {
                 "name": "instance",
                 "requires": ["vpc", "bastion"],
                 "template_path": ".",
             },
             {
                 "name": "db",
                 "requires": ["instance", "vpc", "bastion"],
                 "template_path": ".",
             },
             {"name": "other", "requires": ["db"], "template_path": "."},
         ],
     }
     if extra_config_args:
         config.update(extra_config_args)
     return CfnginContext(config=CfnginConfig.parse_obj(config), **kwargs)
Example #7
0
    def test_execute_plan(self) -> None:
        """Test execute plan."""
        context = CfnginContext(config=self.config)
        context.put_persistent_graph = mock.MagicMock()
        vpc = Stack(definition=generate_definition("vpc", 1), context=context)
        bastion = Stack(
            definition=generate_definition("bastion", 1, requires=[vpc.name]),
            context=context,
        )
        removed = Stack(
            definition=generate_definition("removed", 1, requires=[]), context=context
        )
        context._persistent_graph = Graph.from_steps([Step(removed)])

        calls: List[str] = []

        def _launch_stack(stack: Stack, status: Optional[Status] = None) -> Status:
            calls.append(stack.fqn)
            return COMPLETE

        def _destroy_stack(stack: Stack, status: Optional[Status] = None) -> Status:
            calls.append(stack.fqn)
            return COMPLETE

        graph = Graph.from_steps(
            [
                Step(removed, fn=_destroy_stack),
                Step(vpc, fn=_launch_stack),
                Step(bastion, fn=_launch_stack),
            ]
        )
        plan = Plan(description="Test", graph=graph, context=context)
        plan.context._persistent_graph_lock_code = plan.lock_code  # type: ignore
        plan.execute(walk)

        # the order these are appended changes between python2/3
        self.assertIn("namespace-vpc.1", calls)
        self.assertIn("namespace-bastion.1", calls)
        self.assertIn("namespace-removed.1", calls)
        context.put_persistent_graph.assert_called()

        # order is different between python2/3 so can't compare dicts
        result_graph_dict = context.persistent_graph.to_dict()  # type: ignore
        self.assertEqual(2, len(result_graph_dict))
        self.assertEqual(set(), result_graph_dict.get("vpc.1"))
        self.assertEqual(set(["vpc.1"]), result_graph_dict.get("bastion.1"))
        self.assertIsNone(result_graph_dict.get("namespace-removed.1"))
Example #8
0
 def setUp(self) -> None:
     """Run before tests."""
     self.context = CfnginContext(
         config=CfnginConfig.parse_obj({
             "namespace": "test",
             "cfngin_bucket": "test"
         }))
     self.provider = mock_provider(region="us-east-1")
Example #9
0
 def test_create_template_passes(self) -> None:
     """Test create template passes."""
     ctx = CfnginContext()
     blueprint = Repositories("test_repo", ctx)
     blueprint.resolve_variables(
         [Variable("Repositories", ["repo1", "repo2"], "cfngin")])
     blueprint.create_template()
     self.assertRenderedBlueprint(blueprint)
Example #10
0
def test_stacks_exists(cfngin_context: CfnginContext) -> None:
    """Test CloudFormation Stacks exists."""
    client = cfngin_context.get_session(region="us-east-1").client("cloudformation")
    assert cfngin_context.stacks, "no stacks found in context/config"
    for stack in cfngin_context.stacks:
        assert client.describe_stacks(StackName=stack.fqn)[
            "Stacks"
        ], f"unable to descrive stack: {stack.fqn}"
Example #11
0
 def test_create_template_fails(self) -> None:
     """Test create template fails."""
     ctx = CfnginContext()
     blueprint = Repositories("test_repo", ctx)
     blueprint.resolve_variables(
         [Variable("Repositories", ["repo1", "repo2", "repo3"], "cfngin")])
     blueprint.create_template()
     with self.assertRaises(AssertionError):
         self.assertRenderedBlueprint(blueprint)
Example #12
0
 def setUp(self) -> None:
     """Run before tests."""
     self.context = CfnginContext(
         config=CfnginConfig.parse_obj({"namespace": "namespace"}))
     self.provider = MockProvider()
     self.deploy_action = deploy.Action(
         self.context,
         provider_builder=MockProviderBuilder(
             provider=self.provider),  # type: ignore
     )
Example #13
0
def test_stacks_not_exists(cfngin_context: CfnginContext) -> None:
    """Test CloudFormation Stacks don't exists."""
    client = cfngin_context.get_session(region="us-east-1").client("cloudformation")
    assert cfngin_context.stacks, "no stacks found in context/config"
    for stack in cfngin_context.stacks:
        try:
            assert not client.describe_stacks(StackName=stack.fqn)[
                "Stacks"
            ], f"stack exists: {stack.fqn}"
        except client.exceptions.ClientError as exc:
            assert "does not exist" in str(exc)
Example #14
0
def get_principal_arn(context: CfnginContext) -> str:
    """Return ARN of current session principle."""
    # looking up caller identity
    session = context.get_session()
    sts_client = session.client("sts")
    caller_identity_arn = sts_client.get_caller_identity()["Arn"]
    if caller_identity_arn.split(":")[2] == "iam" and (
        caller_identity_arn.split(":")[5].startswith("user/")
    ):
        return caller_identity_arn  # user arn
    return assumed_role_to_principle(caller_identity_arn)
Example #15
0
    def setUp(self) -> None:
        """Run before tests."""
        self.context = CfnginContext(
            config=CfnginConfig.parse_obj({
                "namespace": "test",
                "cfngin_bucket": "test"
            }))
        self.provider = mock_provider(region="us-east-1")

        self.mock_process = MockProcess()
        self.popen_mock = mock.patch("runway.cfngin.hooks.command.Popen",
                                     return_value=self.mock_process).start()
Example #16
0
def cfngin_context(
    cd_test_dir: Path,
    cfngin_config: CfnginConfig,
    runway_config: RunwayConfig,
    runway_context: RunwayContext,
) -> CfnginContext:
    """Return CFNgin context."""
    return CfnginContext(
        config=cfngin_config,
        config_path=cd_test_dir / "cfngin.yml",
        deploy_environment=runway_context.env,
        parameters=runway_config.deployments[0].parameters,
    )
Example #17
0
def invoke(
    context: CfnginContext,
    *,
    expected_status_code: int = 200,
    function_name: str,
    **_: Any,
) -> bool:
    """Invoke AWS Lambda Function and check the response."""
    LOGGER.info("invoking %s", function_name)
    assert (context.get_session().client("lambda").invoke(
        FunctionName=function_name,
        InvocationType="RequestResponse")["StatusCode"] == expected_status_code
            )
    LOGGER.info("%s returned %s", function_name, expected_status_code)
    return True
Example #18
0
    def setUp(self) -> None:
        """Run before tests."""
        self.count = 0
        self.config = CfnginConfig.parse_obj({"namespace": "namespace"})
        self.context = CfnginContext(config=self.config)

        class FakeLookup(LookupHandler):
            """False Lookup."""

            # pylint: disable=arguments-differ
            @classmethod
            def handle(cls, value: str, *__args: Any, **__kwargs: Any) -> str:  # type: ignore
                """Perform the lookup."""
                return "test"

        register_lookup_handler("noop", FakeLookup)
Example #19
0
    def setUp(self) -> None:
        """Run before tests."""
        self.sd = {"name": "test"}  # pylint: disable=invalid-name
        self.config = CfnginConfig.parse_obj({"namespace": "namespace"})
        self.context = CfnginContext(config=self.config)
        self.stack = Stack(definition=generate_definition("vpc", 1),
                           context=self.context)

        class FakeLookup(LookupHandler):
            """False Lookup."""

            # pylint: disable=arguments-differ,unused-argument
            @classmethod
            def handle(cls, value: str, *__args: Any,
                       **__kwargs: Any) -> str:  # type: ignore
                """Perform the lookup."""
                return "test"

        register_lookup_handler("noop", FakeLookup)
Example #20
0
def delete_prefix(
    context: CfnginContext,
    *,
    bucket_name: str,
    delimiter: str = "/",
    prefix: str,
    **_: Any,
) -> bool:
    """Delete all objects with prefix."""
    if not Bucket(context, bucket_name):
        LOGGER.warning("bucket '%s' does not exist or you do not have access to it")
        return True
    bucket = context.get_session().resource("s3").Bucket(bucket_name)
    LOGGER.info(
        "deleting objects from s3://%s%s%s...",
        bucket_name,
        delimiter,
        prefix,
    )
    bucket.object_versions.filter(Delimiter=delimiter, Prefix=prefix).delete()
    return True
Example #21
0
    def test_path_relative(self) -> None:
        """Test path relative."""
        with self.temp_directory_with_files(["test/test.py"]) as temp_dir:
            results = self.run_hook(
                functions={"MyFunction": {
                    "path": "test"
                }},
                context=CfnginContext(
                    config=CfnginConfig.parse_obj({
                        "namespace": "test",
                        "cfngin_bucket": "test"
                    }),
                    config_path=Path(str(temp_dir.path)),
                ),
            )

        self.assertIsNotNone(results)

        code = results.get("MyFunction")
        self.assertIsInstance(code, Code)
        self.assert_s3_zip_file_list(code.S3Bucket, code.S3Key, ["test.py"])
Example #22
0
 def _get_context(self,
                  extra_config_args: Optional[Dict[str, Any]] = None,
                  **kwargs: Any) -> CfnginContext:
     """Get context."""
     config: Dict[str, Any] = {
         "namespace":
         "namespace",
         "stacks": [
             {
                 "name": "vpc",
                 "template_path": "."
             },
             {
                 "name": "bastion",
                 "template_path": ".",
                 "variables": {
                     "test": "${output vpc::something}"
                 },
             },
             {
                 "name": "db",
                 "template_path": ".",
                 "variables": {
                     "test": "${output vpc::something}",
                     "else": "${output bastion::something}",
                 },
             },
             {
                 "name": "other",
                 "template_path": ".",
                 "variables": {}
             },
         ],
     }
     if extra_config_args:
         config.update(extra_config_args)
     return CfnginContext(config=CfnginConfig.parse_obj(config), **kwargs)
Example #23
0
 def setUp(self) -> None:
     """Run before tests."""
     self.provider = MagicMock()
     self.context = CfnginContext(
         config=CfnginConfig.parse_obj({"namespace": "ns"}))
Example #24
0
                    self.variables["NodeAutoScalingGroupMaxSize"].ref,
                    NoValue,
                ),
                LaunchConfigurationName=nodelaunchconfig.ref(),
                MinSize=self.variables["NodeAutoScalingGroupMinSize"].ref,
                MaxSize=self.variables["NodeAutoScalingGroupMaxSize"].ref,
                VPCZoneIdentifier=self.variables["Subnets"].ref,
                Tags=[
                    autoscaling.Tag(
                        "Name", Sub("${ClusterName}-${NodeGroupName}-Node"), True
                    ),
                    autoscaling.Tag(
                        Sub("kubernetes.io/cluster/${ClusterName}"), "owned", True
                    ),
                ],
                UpdatePolicy=UpdatePolicy(
                    AutoScalingRollingUpdate=AutoScalingRollingUpdate(
                        MinInstancesInService="1", MaxBatchSize="1"
                    )
                ),
            )
        )


# Helper section to enable easy blueprint -> template generation
# (just run `python <thisfile>` to output the json)
if __name__ == "__main__":
    from runway.context import CfnginContext

    print(NodeGroup("test", CfnginContext(parameters={"namespace": "test"})).to_json())
Example #25
0
                AssumeRolePolicyDocument=PolicyDocument(
                    Statement=[
                        Statement(
                            Effect=Allow,
                            Action=[awacs.sts.AssumeRole],
                            Principal=Principal("AWS", TESTING_ACCOUNT_ID),
                        )
                    ]
                ),
                Description="Role used for cross account testing in runway",
                ManagedPolicyArns=["arn:aws:iam::aws:policy/AdministratorAccess"],
                RoleName=Join(
                    "-",
                    [
                        "runway-integration-test-role",
                        self.variables["EnvironmentName"].ref,
                    ],
                ),
            )
        )


if __name__ == "__main__":
    from runway.context import CfnginContext

    print(
        CrossAccountRole(
            "test", CfnginContext(parameters={"namespace": "test"})
        ).to_json()
    )
Example #26
0
                            ],
                        ),
                        Statement(
                            Action=[
                                awacs.dynamodb.GetItem,
                                awacs.dynamodb.PutItem,
                                awacs.dynamodb.DeleteItem,
                            ],
                            Effect=Allow,
                            Resource=[terraformlocktable.get_att("Arn")],
                        ),
                    ],
                ),
            ))
        self.template.add_output(
            Output(
                "PolicyArn",
                Description="Managed policy Arn",
                Value=managementpolicy.ref(),
            ))


# Helper section to enable easy blueprint -> template generation
# (just run `python <thisfile>` to output the json)
if __name__ == "__main__":
    from runway.context import CfnginContext

    print(
        TfState("test",
                CfnginContext(parameters={"namespace": "test"})).to_json())
Example #27
0
 def setUp(self) -> None:
     """Run before tests."""
     self.ctx = CfnginContext()
     self.prov = MagicMock()
     self.blueprint = MagicMock()