def _setUp(self, subfolder):
     session = boto3.Session()
     pill = placebo.attach(session,
                           data_path=os.path.join(self.PLACEBO_PATH,
                                                  subfolder))
     pill.playback()
     self.ssm_client = session.client('ssm')
     SSMParameter.set_ssm_client(self.ssm_client)
    def test_with_valid_client(self):
        """ Test invalid client (without required methods) """

        # pylint: disable=unused-argument,no-self-use
        class MyValidClient(object):
            """ This client has all the required methods """
            def get_parameters(self, *args, **kwargs):
                """ Mock method """
                return {
                    'InvalidParameters': [],
                    'Parameters': [
                        {
                            "Type": "String",
                            "Name": "my_param",
                            "Value": "abc123",
                            "Version": 1
                        },
                    ],
                }

            def get_parameters_by_path(self, *args, **kwargs):
                """ Mock method """
                return {
                    "Parameters": [
                        {
                            "Type": "String",
                            "Name": "/foo/bar/1",
                            "Value": "abc123",
                            "Version": 1
                        },
                        {
                            "Type": "String",
                            "Name": "/foo/bar/2",
                            "Value": "abc123",
                            "Version": 1
                        },
                    ]
                }

        client = MyValidClient()
        SSMParameter.set_ssm_client(client)

        param = SSMParameter("my_param")
        self.assertEqual(param.value, self.PARAM_VALUE)

        group = SSMParameterGroup()
        param = group.parameter("my_param")
        self.assertEqual(param.value, self.PARAM_VALUE)

        params = group.parameters("/foo/bar/")
        self.assertEqual(len(params), 2)
        for param in params:
            self.assertEqual(param.value, self.PARAM_VALUE)
    def test_with_placebo(self):
        """ Test that set_ssm_client works fine with Placebo """
        session = boto3.Session()
        pill = placebo.attach(session, data_path=self.PLACEBO_PATH)
        pill.playback()

        client = session.client('ssm')

        SSMParameter.set_ssm_client(client)

        param = SSMParameter("my_param")
        self.assertEqual(param.value, self.PARAM_VALUE)
Example #4
0
 def test_should_refresh(self):
     # without max age
     cache = SSMParameter("my_param")
     self.assertFalse(cache._should_refresh())
     # with max age and no data
     cache = SSMParameter("my_param", max_age=10)
     self.assertTrue(cache._should_refresh())
     # with max age and last refreshed date OK
     cache._last_refresh_time = datetime.utcnow()
     self.assertFalse(cache._should_refresh())
     # with max age and last refreshed date KO
     cache._last_refresh_time = datetime.utcnow() - timedelta(seconds=20)
     self.assertTrue(cache._should_refresh())
    def test_with_illegal_client(self):
        """ Test invalid client (without required methods) """
        with self.assertRaises(TypeError):
            SSMParameter.set_ssm_client(42)

        # pylint: disable=too-few-public-methods
        class MyInvalidClient(object):
            """ This client only has get_parameters """
            def get_parameters(self):
                """ Empty method """

        with self.assertRaises(TypeError):
            client = MyInvalidClient()
            SSMParameter.set_ssm_client(client)
    def test_versions_invalid(self):
        """ Test invalid version """

        name = "my_param"

        with self.assertRaises(InvalidVersionError):
            SSMParameter("%s:hello" % name)

        with self.assertRaises(InvalidVersionError):
            SSMParameter("%s:0" % name)

        with self.assertRaises(InvalidVersionError):
            SSMParameter("%s:-1" % name)

        with self.assertRaises(InvalidVersionError):
            SSMParameter("%s:" % name)
Example #7
0
 def setUp(self):
     """ Create params/groups for each test """
     names = ["my_param", "my_grouped_param"]
     self._create_params(names)
     self.cache = SSMParameter("my_param")
     self.group = SSMParameterGroup()
     self.grouped_param = self.group.parameter("my_grouped_param")
Example #8
0
    def test_creation(self):
        # single string
        cache = SSMParameter("my_param")
        self.assertTrue(cache._with_decryption)
        self.assertIsNone(cache._max_age)
        self.assertIsNone(cache._last_refresh_time)
        # invalid params
        with self.assertRaises(TypeError):
            SSMParameter()
        with self.assertRaises(ValueError):
            SSMParameter(None)

        group = SSMParameterGroup()
        parameter = group.parameter("my_param")
        with self.assertRaises(TypeError):
            group.parameter()
Example #9
0
def get_config(config_key):
    """
Retrieve the configuration from the SSM parameter store
The config always returns a tuple (value, rate)
value: requested configuration
rate: the injection probability (default 1 --> 100%)

How to use::

    >>> import os
    >>> from chaos_lib import get_config
    >>> os.environ['FAILURE_INJECTION_PARAM'] = 'chaoslambda.config'
    >>> get_config('delay')
    (400, 1)
    >>> get_config('exception_msg')
    ('I really failed seriously', 1)
    >>> get_config('error_code')
    (404, 1)
    """
    param = SSMParameter(os.environ['FAILURE_INJECTION_PARAM'])
    try:
        value = json.loads(param.value)
        if not value["isEnabled"]:
            return 0, 0
        return value[config_key], value.get('rate', 1)
    except InvalidParameterError as ex:
        # key does not exist in SSM
        raise InvalidParameterError("{} is not a valid SSM config".format(ex))
    except KeyError as ex:
        # not a valid Key in the SSM configuration
        raise KeyError("key {} not valid or found in SSM config".format(ex))
Example #10
0
    def test_creation(self):
        """ Test regular creation """
        # single string
        param = SSMParameter("my_param")
        self.assertTrue(param._with_decryption)
        self.assertIsNone(param._max_age)
        self.assertIsNone(param._last_refresh_time)
        # invalid params
        with self.assertRaises(TypeError):
            SSMParameter()  # pylint: disable=no-value-for-parameter
        with self.assertRaises(ValueError):
            SSMParameter(None)

        group = SSMParameterGroup()
        param = group.parameter("my_param")
        with self.assertRaises(TypeError):
            group.parameter()  # pylint: disable=no-value-for-parameter
Example #11
0
 def test_creation(self):
     # single string
     cache = SSMParameter("my_param")
     self.assertEqual(1, len(cache._names))
     self.assertTrue(cache._with_decryption)
     self.assertIsNone(cache._max_age)
     self.assertIsNone(cache._last_refresh_time)
     # list of params
     cache = SSMParameter(["my_param_1", "my_param_2"])
     self.assertEqual(2, len(cache._names))
     # invalid params
     with self.assertRaises(ValueError):
         SSMParameter()
     with self.assertRaises(ValueError):
         SSMParameter(None)
     with self.assertRaises(ValueError):
         SSMParameter([])
Example #12
0
    def test_main_lambda_handler(self):
        cache = SSMParameter("my_param")

        def lambda_handler(event, context):
            secret_value = cache.value()
            return 'Hello from Lambda with secret %s' % secret_value

        lambda_handler(None, None)
Example #13
0
 def test_string_list(self):
     """ Test StringList expiration """
     param = SSMParameter("my_params_list")
     values = param.value
     self.assertTrue(isinstance(values, list))
     self.assertEqual(len(values), self.PARAM_LIST_COUNT)
     for value in values:
         self.assertEqual(value, self.PARAM_VALUE)
Example #14
0
    def test_main_with_explicit_refresh(self):
        cache = SSMParameter("my_param")  # will not expire

        class InvalidCredentials(Exception):
            pass

        def do_something():
            my_value = cache.value()
            if my_value == self.PARAM_VALUE:
                raise InvalidCredentials()

        try:
            do_something()
        except InvalidCredentials:
            # manually update value
            self._create_params(["my_param"], "new_value")
            cache.refresh()  # force refresh
            do_something()  # won't fail anymore
Example #15
0
    def test_main_with_explicit_refresh(self):
        """ Test explicit refresh case """
        param = SSMParameter("my_param")  # will not expire

        class InvalidCredentials(Exception):
            """ Mock exception class """

        def do_something():
            """ Raise an exception until the value has changed """
            my_value = param.value
            if my_value == self.PARAM_VALUE:
                raise InvalidCredentials()

        try:
            do_something()
        except InvalidCredentials:
            # manually update value
            self._create_params(["my_param"], "new_value")
            param.refresh()  # force refresh
            do_something()  # won't fail anymore
Example #16
0
    def _setUp(self, class_name, test_name):
        os.environ['CHAOS_PARAM'] = SSM_CONFIG_FILE
        session = boto3.Session()
        dir_name = os.path.join(self.PLACEBO_PATH, class_name, test_name)
        pill = placebo.attach(session,
                              data_path=os.path.join(self.PLACEBO_PATH,
                                                     class_name, test_name))

        if PLACEBO_MODE == "record":
            try:
                os.makedirs(dir_name)
            except FileExistsError:
                print("Directory already exists")
            print("Recording")
            pill.record()
        else:
            pill.playback()

        self.ssm_client = session.client('ssm')
        SSMParameter.set_ssm_client(self.ssm_client)
Example #17
0
    def test_main_lambda_handler(self):
        """ Test simple AWS Lambda handler """
        cache = SSMParameter("my_param")

        def lambda_handler(event, context):
            """ Simple Lambda handler that just prints a string """
            print(event, context)
            secret_value = cache.value
            return 'Hello from Lambda with secret %s' % secret_value

        return_value = lambda_handler(None, None)
        expected_value = 'Hello from Lambda with secret %s' % self.PARAM_VALUE
        self.assertEqual(return_value, expected_value)
def get_config(config_key):
    param = SSMParameter(os.environ['FAILURE_INJECTION_PARAM'])
    try:
        value = json.loads(param.value)
        if not value["isEnabled"]:
            return 0, 1
        return value[config_key], value.get('rate', 1)
    except InvalidParameterError as e:
        # key does not exist in SSM
        raise InvalidParameterError("{} does not exist in SSM".format(e))
    except KeyError as e:
        # not a valid Key in the SSM configuration
        raise KeyError(
            "{} is not a valid Key in the SSM configuration".format(e))
    def test_versions_unexisting(self):
        """ Test non existing version """
        method_name = sys._getframe().f_code.co_name
        self._setUp(method_name)

        name = method_name
        self._create_or_update_param(name)

        param = SSMParameter("%s:10" % name)

        with self.assertRaises(InvalidParameterError):
            print(param.value)

        self._delete_param(name)
    def test_select_versions(self):
        """ Test version selection """

        method_name = sys._getframe().f_code.co_name
        self._setUp(method_name)

        name = method_name
        self._create_or_update_param(name)

        param = SSMParameter("%s:1" % name)

        self.assertEqual(param.value, self.PARAM_VALUE)
        self.assertEqual(param.version, 1)

        # this will update the value and create version 2
        self._create_or_update_param(name, self.PARAM_VALUE_V2)

        param.refresh()

        self.assertEqual(param.value, self.PARAM_VALUE)
        self.assertEqual(param.version, 1)

        self._delete_param(name)
    def test_update_versions(self):
        """ Test version update """

        method_name = sys._getframe().f_code.co_name
        self._setUp(method_name)

        name = method_name
        self._create_or_update_param(name)

        param = SSMParameter(name)

        self.assertEqual(param.version, 1)
        self.assertEqual(param.value, self.PARAM_VALUE)

        # this will update the value and create version 2
        self._create_or_update_param(name, self.PARAM_VALUE_V2)

        param.refresh()

        # refreshing should give you version 2
        self.assertEqual(param.version, 2)
        self.assertEqual(param.value, self.PARAM_VALUE_V2)

        self._delete_param(name)
Example #22
0
 def test_main_with_multiple_params(self):
     cache = SSMParameter(["my_param_1", "my_param_2", "my_param_3"])
     # one by one
     my_value_1 = cache.value("my_param_1")
     my_value_2 = cache.value("my_param_2")
     my_value_3 = cache.value("my_param_3")
     self.assertEqual(my_value_1, self.PARAM_VALUE)
     self.assertEqual(my_value_2, self.PARAM_VALUE)
     self.assertEqual(my_value_3, self.PARAM_VALUE)
     with self.assertRaises(TypeError):
         cache.value()  # name is required
     # or all together
     my_value_1, my_value_2, my_value_3 = cache.values()
     self.assertEqual(my_value_1, self.PARAM_VALUE)
     self.assertEqual(my_value_2, self.PARAM_VALUE)
     self.assertEqual(my_value_3, self.PARAM_VALUE)
     # or a subset
     my_value_1, my_value_2 = cache.values(["my_param_1", "my_param_2"])
     self.assertEqual(my_value_1, self.PARAM_VALUE)
     self.assertEqual(my_value_2, self.PARAM_VALUE)
Example #23
0
    def create(cls,
               project,
               stage_name,
               param_name,
               param_value,
               param_type,
               param_desc=None,
               overwrite=False,
               tags={}):
        param_path = create_param_path(project, stage_name, param_name)
        if not param_value:
            return None

        if param_type not in VALID_PARAM_TYPES:
            raise ParamCreateError(
                "Invalid parameter type: {}".format(param_type))

        try:
            tag_list = [{"Key": k, "Value": v} for k, v in tags.items()]
            param_resp = ssm.put_parameter(Name=param_path,
                                           Description=param_desc,
                                           Value=param_value,
                                           Type=param_type,
                                           Overwrite=overwrite)
            if len(tag_list):
                tag_resp = ssm.add_tags_to_resource(ResourceType="Parameter",
                                                    ResourceId=param_path,
                                                    Tags=tag_list)
        except ssm.exceptions.ClientError as e:
            raise ParamCreateError(str(e))

        ssm_param = SSMParameter(param_path)
        param = Param(ssm_param)
        if not param.exists():
            raise ParamCreateError(
                "Something went wrong creating {}".format(param_path))
        return param
Example #24
0
 def test_main_with_expiration(self):
     cache = SSMParameter("my_param",
                          max_age=300)  # 5 minutes expiration time
     my_value = cache.value()
     self.assertEqual(my_value, self.PARAM_VALUE)
Example #25
0
 def test_main(self):
     cache = SSMParameter("my_param")
     my_value = cache.value()
     self.assertEqual(my_value, self.PARAM_VALUE)
Example #26
0
 def test_unexisting(self):
     cache = SSMParameter("my_param_invalid_name")
     with self.assertRaises(InvalidParam):
         cache.value()
Example #27
0
import boto3
from pathlib import Path
from ssm_cache import SSMParameterGroup, SSMParameter, InvalidParameterError

VALID_PARAM_TYPES = ["String", "SecureString", "StringList"]

# make sure we're using the same client as the SSMParameter objects
ssm = boto3.client('ssm')
SSMParameter.set_ssm_client(ssm)


def get_stages(project):
    project_path = (Path("/") / project).as_posix()
    group = SSMParameterGroup(base_path=project_path)
    param_paths = [Path(x.full_name) for x in group.parameters("/")]
    stage_names = set(p.parts[2] for p in param_paths)
    return [Stage(project, x) for x in stage_names]


def create_param_path(project, stage_name, param_name):
    return (Path("/") / project / stage_name / param_name).as_posix()


class ParameterNotFound(Exception):
    pass


class ParamSchemaValidationError(Exception):
    def __init__(self, message=None, errors=[]):
        super(ParamSchemaValidationError, self).__init__(message)
        self.errors = errors
Example #28
0
 def test_not_configured(self):
     cache = SSMParameter(["param_1", "param_2"])
     with self.assertRaises(InvalidParam):
         cache.value("param_3")
Example #29
0
 def test_main_without_encryption(self):
     cache = SSMParameter("my_param", with_decryption=False)
     my_value = cache.value()
     self.assertEqual(my_value, self.PARAM_VALUE)
Example #30
0
 def setUp(self):
     names = ["my_param", "my_grouped_param"]
     self._create_params(names)
     self.cache = SSMParameter("my_param")
     self.group = SSMParameterGroup()
     self.grouped_param = self.group.parameter("my_grouped_param")