Beispiel #1
0
    def test_get_parameter_values(self):
        """Test get parameter values."""
        class TestBlueprint(Blueprint):
            """Test blueprint."""

            VARIABLES = {
                "Param1": {
                    "type": int
                },
                "Param2": {
                    "type": CFNString
                },
            }

        blueprint = TestBlueprint(name="test", context=MagicMock())
        variables = [
            Variable("Param1", 1, 'cfngin'),
            Variable("Param2", "Value", 'cfngin')
        ]
        blueprint.resolve_variables(variables)
        variables = blueprint.get_variables()
        self.assertEqual(len(variables), 2)
        parameters = blueprint.get_parameter_values()
        self.assertEqual(len(parameters), 1)
        self.assertEqual(parameters["Param2"], "Value")
Beispiel #2
0
    def test_default(self, runway_context):
        """Test resolution of a default value."""
        name = "/test/param"
        value = "test value"
        stubber = runway_context.add_stubber("ssm")
        var = Variable(
            "test_var",
            "${ssm /test/invalid::load=json, default=${ssm %s}}" % name,
            variable_type="runway",
        )

        stubber.add_response(
            "get_parameter",
            get_parameter_response(name, value),
            get_parameter_request(name),
        )
        stubber.add_client_error(
            "get_parameter",
            "ParameterNotFound",
            expected_params=get_parameter_request("/test/invalid"),
        )

        with stubber as stub:
            var.resolve(context=runway_context)
            assert var.value == value
            stub.assert_no_pending_responses()
Beispiel #3
0
    def test_resolve_variables(self):
        """Test resolve variables."""
        class TestBlueprint(Blueprint):
            """Test blueprint."""

            VARIABLES = {
                "Param1": {
                    "default": 0,
                    "type": int
                },
                "Param2": {
                    "type": str
                },
            }

        blueprint = TestBlueprint(name="test", context=MagicMock())
        variables = [
            Variable("Param1", 1, 'cfngin'),
            Variable("Param2", "${output other-stack::Output}", 'cfngin'),
            Variable("Param3", 3, 'cfngin'),
        ]

        variables[1]._value._resolve("Test Output")

        blueprint.resolve_variables(variables)
        self.assertEqual(blueprint.resolved_variables["Param1"], 1)
        self.assertEqual(blueprint.resolved_variables["Param2"], "Test Output")
        self.assertIsNone(blueprint.resolved_variables.get("Param3"))
Beispiel #4
0
    def test_value_complex_str(self):
        """Multiple lookups should be usable within a single string."""
        var = Variable('test', 'the ${env what} was ${env test}ful', 'runway')
        var.resolve(CONTEXT)

        self.assertEqual(
            var.value, 'the {} was {}ful'.format(VALUE['what'], VALUE['test']))
Beispiel #5
0
    def to_json(self, variables=None):
        """Render the blueprint and return the template in json form.

        Args:
            variables (Optional[Dict[str, Any]]):
                Dictionary providing/overriding variable values.

        Returns:
            str: Rhe rendered CFN JSON template.

        """
        variables_to_resolve = []
        if variables:
            for key, value in variables.items():
                variables_to_resolve.append(Variable(key, value, 'cfngin'))
        for k in self.get_parameter_definitions():
            if not variables or k not in variables:
                # The provided value for a CFN parameter has no effect in this
                # context (generating the CFN template), so any string can be
                # provided for its value - just needs to be something
                variables_to_resolve.append(
                    Variable(k, 'unused_value', 'cfngin'))
        self.resolve_variables(variables_to_resolve)

        return self.render_template()[1]
Beispiel #6
0
 def test_simple_lookup(self) -> None:
     """Test simple lookup."""
     var = Variable("Param1", "${test query}")
     assert isinstance(var._value, VariableValueLookup)
     var.resolve(MagicMock(), MagicMock())
     assert var.resolved is True
     assert var.value == "resolved"
Beispiel #7
0
 def test_dependencies(self, mocker: MockerFixture) -> None:
     """Test dependencies."""
     assert Variable("Param", "val").dependencies == set()
     mocker.patch.object(VariableValue,
                         "parse_obj",
                         return_value=MagicMock(dependencies={"test"}))
     assert Variable("Param", "val").dependencies == {"test"}
Beispiel #8
0
    def test_value_lookup_to_dict(self):
        """Variable lookups should be resolvable to a dict value."""
        var = Variable('test', '${env dict_val}', 'runway')
        var.resolve(CONTEXT)

        # need to use data prop to compare the MutableMap contents
        self.assertEqual(var.value.data, VALUE['dict_val'])
Beispiel #9
0
    def test_value_complex_str(self):
        """Multiple lookups should be usable within a single string."""
        var = Variable("test", "the ${env what} was ${env test}ful", "runway")
        var.resolve(CONTEXT)

        self.assertEqual(
            var.value, "the {} was {}ful".format(VALUE["what"], VALUE["test"]))
Beispiel #10
0
    def test_loaded_value(self, runway_context: MockRunwayContext) -> None:
        """Test resolution of a JSON value."""
        name = "/test/param"
        raw_value = {
            "nested": {
                "bool": True,
                "nest_key": "nested_val"
            },
            "test_key": "test_val",
        }
        stubber = runway_context.add_stubber("ssm")
        parsers = ["json", "yaml"]
        tests = [
            {
                "lookup": "${{ssm {name}::load={parser}}}",
                "expected": raw_value
            },
            {
                "lookup":
                "${{ssm {name}::load={parser},transform=str,indent=2}}",
                "expected": json.dumps(json.dumps(raw_value, indent=2)),
            },
            {
                "lookup": "${{ssm {name}::load={parser},get=nested}}",
                "expected": raw_value["nested"],
            },
            {
                "lookup":
                "${{ssm {name}::load={parser},get=nested.bool,transform=str}}",
                "expected": json.dumps("True"),
            },
        ]

        for parser in parsers:
            for test in tests:
                var = Variable(
                    "test_var.{}".format(parser),
                    test["lookup"].format(name=name,
                                          parser=parser),  # type: ignore
                    variable_type="runway",
                )
                if parser == "json":
                    dumped_value = json.dumps(raw_value)
                elif parser == "yaml":
                    dumped_value = yaml.dump(raw_value)
                else:
                    raise ValueError

                stubber.add_response(
                    "get_parameter",
                    get_parameter_response(name, dumped_value),
                    get_parameter_request(name),
                )

                with stubber as stub:
                    var.resolve(context=runway_context)
                    assert var.value == test["expected"]
                    stub.assert_no_pending_responses()
Beispiel #11
0
 def test_legacy_valid_hook_data(self, cfngin_context):
     """Test valid hook data."""
     cfngin_context.set_hook_data('fake_hook', {'result': 'good'})
     variable = Variable('test',
                         "${hook_data fake_hook::result}",
                         variable_type='cfngin')
     with pytest.warns(DeprecationWarning):
         variable.resolve(cfngin_context)
     assert variable.value == 'good'
Beispiel #12
0
 def test_multiple_lookup_string(self, mocker: MockerFixture) -> None:
     """Test multiple lookup string."""
     var = Variable("Param1", "url://${test query0}@${test query1}")
     assert isinstance(var._value, VariableValueConcatenation)
     mocker.patch.object(MockLookupHandler, "side_effect",
                         ["resolved0", "resolved1"])
     var.resolve(MagicMock(), MagicMock())
     assert var.resolved is True
     assert var.value == "url://resolved0@resolved1"
Beispiel #13
0
 def test_legacy_valid_hook_data(self, cfngin_context):
     """Test valid hook data."""
     cfngin_context.set_hook_data("fake_hook", {"result": "good"})
     variable = Variable("test",
                         "${hook_data fake_hook::result}",
                         variable_type="cfngin")
     with pytest.warns(DeprecationWarning):
         variable.resolve(cfngin_context)
     assert variable.value == "good"
Beispiel #14
0
    def test_value_simple_str_lookup(self):
        """Test value for simple str lookup."""
        var = Variable("test", "${env test}", "runway")

        self.assertFalse(var.resolved)

        var.resolve(CONTEXT)

        self.assertTrue(var.resolved)
        self.assertEqual(var.value, VALUE["test"])
Beispiel #15
0
    def test_resolve_variable_provided_resolved(self):
        """Test resolve variable provided resolved."""
        var_name = "testVar"
        var_def = {"type": str}
        provided_variable = Variable(var_name, "${mock 1}", "cfngin")
        provided_variable.resolve(context=MagicMock(), provider=MagicMock())
        blueprint_name = "testBlueprint"

        value = resolve_variable(var_name, var_def, provided_variable, blueprint_name)
        self.assertEqual(value, "1")
Beispiel #16
0
    def test_value_simple_str_lookup(self):
        """Test value for simple str lookup."""
        var = Variable('test', '${env test}', 'runway')

        self.assertFalse(var.resolved)

        var.resolve(CONTEXT)

        self.assertTrue(var.resolved)
        self.assertEqual(var.value, VALUE['test'])
Beispiel #17
0
    def test_legacy_invalid_hook_data(self, cfngin_context):
        """Test invalid hook data."""
        cfngin_context.set_hook_data("fake_hook", {"result": "good"})
        variable = Variable("test",
                            "${hook_data fake_hook::bad_key}",
                            variable_type="cfngin")
        with pytest.raises(FailedVariableLookup) as err, pytest.warns(
                DeprecationWarning):
            variable.resolve(cfngin_context)

        assert "ValueError" in str(err.value)
Beispiel #18
0
 def test_resolve(self, mocker: MockerFixture) -> None:
     """Test resolve."""
     context = MagicMock()
     provider = MagicMock()
     obj = Variable("Param", "val")
     mock_resolve = mocker.patch.object(obj._value, "resolve")
     assert not obj.resolve(context, provider, kwarg="something")
     mock_resolve.assert_called_once_with(context,
                                          provider=provider,
                                          variables=None,
                                          kwarg="something")
Beispiel #19
0
    def test_not_found(self, cfngin_context):
        """Test value not found and no default."""
        variable = Variable('test',
                            '${hook_data fake_hook.bad.result}',
                            variable_type='cfngin')
        with pytest.raises(FailedVariableLookup) as err:
            variable.resolve(cfngin_context)

        assert 'ValueError' in str(err.value)
        assert 'Could not find a value for "fake_hook.bad.result"' in str(
            err.value)
Beispiel #20
0
    def test_legacy_invalid_hook_data(self, cfngin_context):
        """Test invalid hook data."""
        cfngin_context.set_hook_data('fake_hook', {'result': 'good'})
        variable = Variable('test',
                            "${hook_data fake_hook::bad_key}",
                            variable_type='cfngin')
        with pytest.raises(FailedVariableLookup) as err, \
                pytest.warns(DeprecationWarning):
            variable.resolve(cfngin_context)

        assert 'ValueError' in str(err.value)
Beispiel #21
0
    def test_legacy_bad_value_hook_data(self, cfngin_context):
        """Test bad value hook data."""
        variable = Variable('test',
                            "${hook_data fake_hook::bad_key}",
                            variable_type='cfngin')

        with pytest.raises(FailedVariableLookup) as err, \
                pytest.warns(DeprecationWarning):
            variable.resolve(cfngin_context)

        assert 'ValueError' in str(err.value)
Beispiel #22
0
    def test_not_found(self, cfngin_context: MockCFNginContext) -> None:
        """Test value not found and no default."""
        variable = Variable("test",
                            "${hook_data fake_hook.bad.result}",
                            variable_type="cfngin")
        with pytest.raises(FailedVariableLookup) as err:
            variable.resolve(cfngin_context)

        assert str(err.value) == (
            f'Could not resolve lookup "{variable._raw_value}" for variable "{variable.name}"'
        )
        assert "Could not find a value for" in str(err.value.__cause__)
Beispiel #23
0
 def test_resolve_failed(self, mocker: MockerFixture) -> None:
     """Test resolve FailedLookup."""
     context = MagicMock()
     provider = MagicMock()
     obj = Variable("Param", "val")
     lookup_error = FailedLookup("something",
                                 KeyError("cause"))  # type: ignore
     mocker.patch.object(obj._value, "resolve", side_effect=lookup_error)
     with pytest.raises(FailedVariableLookup) as excinfo:
         obj.resolve(context, provider, kwarg="something")
     assert excinfo.value.cause == lookup_error
     assert excinfo.value.variable == obj
Beispiel #24
0
 def test_multiple_lookup_dict(self, mocker: MockerFixture) -> None:
     """Test multiple lookup dict."""
     mocker.patch.object(MockLookupHandler, "side_effect",
                         ["resolved0", "resolved1"])
     value = {
         "something": "${test query0}",
         "other": "${test query1}",
     }
     var = Variable("Param1", value)
     assert isinstance(var._value, VariableValueDict)
     var.resolve(MagicMock(), MagicMock())
     assert var.value == {"something": "resolved0", "other": "resolved1"}
Beispiel #25
0
    def test_resolve_variable_allowed_values(self):
        """Test resolve variable allowed values."""
        var_name = "testVar"
        var_def = {"type": str, "allowed_values": ["allowed"]}
        provided_variable = Variable(var_name, "not_allowed", "cfngin")
        blueprint_name = "testBlueprint"
        with self.assertRaises(ValueError):
            resolve_variable(var_name, var_def, provided_variable, blueprint_name)

        provided_variable = Variable(var_name, "allowed", "cfngin")
        value = resolve_variable(var_name, var_def, provided_variable, blueprint_name)
        self.assertEqual(value, "allowed")
Beispiel #26
0
 def test_j2_to_json(self):
     """Verify jinja2 template parsing."""
     expected_json = json.dumps(
         {
             "AWSTemplateFormatVersion": "2010-09-09",
             "Description": "TestTemplate",
             "Parameters": {
                 "Param1": {
                     "Type": "String"
                 },
                 "Param2": {
                     "Default": "default",
                     "Type": "CommaDelimitedList"
                 },
             },
             "Resources": {
                 "Dummy": {
                     "Type": "AWS::CloudFormation::WaitConditionHandle"
                 }
             },
             "Outputs": {
                 "DummyId": {
                     "Value": "dummy-bar-param1val-foo-1234"
                 }
             },
         },
         sort_keys=True,
         indent=4,
     )
     blueprint = RawTemplateBlueprint(
         name="stack1",
         context=mock_context(
             extra_config_args={
                 "stacks": [{
                     "name": "stack1",
                     "template_path": "unused",
                     "variables": {
                         "Param1": "param1val",
                         "bar": "foo"
                     },
                 }]
             },
             environment={"foo": "bar"},
         ),
         raw_template_path=RAW_J2_TEMPLATE_PATH,
     )
     blueprint.resolve_variables([
         Variable("Param1", "param1val", "cfngin"),
         Variable("bar", "foo", "cfngin"),
     ])
     self.assertEqual(expected_json, blueprint.to_json())
Beispiel #27
0
    def test_basic(self, cfngin_context, runway_context):
        """Test resolution of a basic lookup."""
        name = "/test/param"
        value = "test value"
        cfngin_stubber = cfngin_context.add_stubber("ssm")
        runway_stubber = runway_context.add_stubber("ssm")
        cfngin_var = Variable("test_var",
                              "${ssm %s}" % name,
                              variable_type="cfngin")
        runway_var = Variable("test_var",
                              "${ssm %s}" % name,
                              variable_type="runway")

        for stubber in [cfngin_stubber, runway_stubber]:
            stubber.add_response(
                "get_parameter",
                get_parameter_response(name, value),
                get_parameter_request(name),
            )

        with cfngin_stubber as cfn_stub, runway_stubber as rw_stub:
            cfngin_var.resolve(context=cfngin_context)
            assert cfngin_var.value == value

            runway_var.resolve(context=runway_context)
            assert runway_var.value == value

        cfn_stub.assert_no_pending_responses()
        rw_stub.assert_no_pending_responses()
Beispiel #28
0
def test_resolve_variable_allowed_values() -> None:
    """Test resolve_variable."""
    var_name = "testVar"
    var_def: BlueprintVariableTypeDef = {"type": str, "allowed_values": ["allowed"]}
    with pytest.raises(ValueError):
        resolve_variable(
            var_name, var_def, Variable(var_name, "not_allowed", "cfngin"), "test"
        )
    assert (
        resolve_variable(
            var_name, var_def, Variable(var_name, "allowed", "cfngin"), "test"
        )
        == "allowed"
    )
Beispiel #29
0
    def test_basic(self, cfngin_context, runway_context):
        """Test resolution of a basic lookup."""
        name = '/test/param'
        value = 'test value'
        cfngin_stubber = cfngin_context.add_stubber('ssm')
        runway_stubber = runway_context.add_stubber('ssm')
        cfngin_var = Variable('test_var',
                              '${ssm %s}' % name,
                              variable_type='cfngin')
        runway_var = Variable('test_var',
                              '${ssm %s}' % name,
                              variable_type='runway')

        for stubber in [cfngin_stubber, runway_stubber]:
            stubber.add_response('get_parameter',
                                 get_parameter_response(name, value),
                                 get_parameter_request(name))

        with cfngin_stubber as cfn_stub, runway_stubber as rw_stub:
            cfngin_var.resolve(context=cfngin_context)
            assert cfngin_var.value == value

            runway_var.resolve(context=runway_context)
            assert runway_var.value == value

        cfn_stub.assert_no_pending_responses()
        rw_stub.assert_no_pending_responses()
Beispiel #30
0
    def test_resolve_variables_lookup_returns_non_string_invalid_combo(self):
        """Test resolve variables lookup returns non string invalid combo."""
        def return_list_something(*_args, **_kwargs):
            """Return list something."""
            return ["something"]

        register_lookup_handler("custom", return_list_something)
        variable = Variable(
            "Param1",
            "${custom non-string-return-val},${output some-stack::Output}",
            'cfngin')
        variable._value[0].resolve({}, {})
        with self.assertRaises(InvalidLookupCombination):
            variable.value()  # pylint: disable=not-callable