diff --git a/moto/ssm/models.py b/moto/ssm/models.py index 3344623dd..f1aac336b 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -28,11 +28,14 @@ class Parameter(BaseModel): return value[len(prefix):] def response_object(self, decrypt=False): - return { + r = { 'Name': self.name, 'Type': self.type, 'Value': self.decrypt(self.value) if decrypt else self.value } + if self.keyid: + r['KeyId'] = self.keyid + return r class SimpleSystemManagerBackend(BaseBackend): @@ -46,6 +49,12 @@ class SimpleSystemManagerBackend(BaseBackend): except KeyError: pass + def get_all_parameters(self): + result = [] + for k, _ in self._parameters.items(): + result.append(self._parameters[k]) + return result + def get_parameters(self, names, with_decryption): result = [] for name in names: diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py index ee21d7380..09fe6d0c2 100644 --- a/moto/ssm/responses.py +++ b/moto/ssm/responses.py @@ -43,6 +43,60 @@ class SimpleSystemManagerResponse(BaseResponse): return json.dumps(response) + def describe_parameters(self): + page_size = 10 + filters = self._get_param('Filters') + token = self._get_param('NextToken') + if hasattr(token, 'strip'): + token = token.strip() + if not token: + token = '0' + token = int(token) + + result = self.ssm_backend.get_all_parameters() + response = { + 'Parameters': [], + } + + end = token + page_size + for parameter in result[token:]: + param_data = parameter.response_object(False) + add = False + + if filters: + for filter in filters: + if filter['Key'] == 'Name': + k = param_data['Name'] + for v in filter['Values']: + if k.startswith(v): + add = True + break + elif filter['Key'] == 'Type': + k = param_data['Type'] + for v in filter['Values']: + if k == v: + add = True + break + elif filter['Key'] == 'KeyId': + k = param_data.get('KeyId') + if k: + for v in filter['Values']: + if k == v: + add = True + break + else: + add = True + + if add: + response['Parameters'].append(param_data) + + token = token + 1 + if len(response['Parameters']) == page_size: + response['NextToken'] = str(end) + break + + return json.dumps(response) + def put_parameter(self): name = self._get_param('Name') description = self._get_param('Description') diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index 6b8a1a369..60a027933 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -47,6 +47,141 @@ def test_put_parameter(): response['Parameters'][0]['Type'].should.equal('String') +@mock_ssm +def test_describe_parameters(): + client = boto3.client('ssm', region_name='us-east-1') + + client.put_parameter( + Name='test', + Description='A test parameter', + Value='value', + Type='String') + + response = client.describe_parameters() + + len(response['Parameters']).should.equal(1) + response['Parameters'][0]['Name'].should.equal('test') + response['Parameters'][0]['Type'].should.equal('String') + + +@mock_ssm +def test_describe_parameters_paging(): + client = boto3.client('ssm', region_name='us-east-1') + + for i in range(50): + client.put_parameter( + Name="param-%d" % i, + Value="value-%d" % i, + Type="String" + ) + + response = client.describe_parameters() + len(response['Parameters']).should.equal(10) + response['NextToken'].should.equal('10') + + response = client.describe_parameters(NextToken=response['NextToken']) + len(response['Parameters']).should.equal(10) + response['NextToken'].should.equal('20') + + response = client.describe_parameters(NextToken=response['NextToken']) + len(response['Parameters']).should.equal(10) + response['NextToken'].should.equal('30') + + response = client.describe_parameters(NextToken=response['NextToken']) + len(response['Parameters']).should.equal(10) + response['NextToken'].should.equal('40') + + response = client.describe_parameters(NextToken=response['NextToken']) + len(response['Parameters']).should.equal(10) + response['NextToken'].should.equal('50') + + response = client.describe_parameters(NextToken=response['NextToken']) + len(response['Parameters']).should.equal(0) + ''.should.equal(response.get('NextToken', '')) + + +@mock_ssm +def test_describe_parameters_filter_names(): + client = boto3.client('ssm', region_name='us-east-1') + + for i in range(50): + p = { + 'Name': "param-%d" % i, + 'Value': "value-%d" % i, + 'Type': "String" + } + if i % 5 == 0: + p['Type'] = 'SecureString' + p['KeyId'] = 'a key' + client.put_parameter(**p) + + response = client.describe_parameters(Filters=[ + { + 'Key': 'Name', + 'Values': ['param-22'] + }, + ]) + len(response['Parameters']).should.equal(1) + response['Parameters'][0]['Name'].should.equal('param-22') + response['Parameters'][0]['Type'].should.equal('String') + ''.should.equal(response.get('NextToken', '')) + + +@mock_ssm +def test_describe_parameters_filter_type(): + client = boto3.client('ssm', region_name='us-east-1') + + for i in range(50): + p = { + 'Name': "param-%d" % i, + 'Value': "value-%d" % i, + 'Type': "String" + } + if i % 5 == 0: + p['Type'] = 'SecureString' + p['KeyId'] = 'a key' + client.put_parameter(**p) + + + response = client.describe_parameters(Filters=[ + { + 'Key': 'Type', + 'Values': ['SecureString'] + }, + ]) + len(response['Parameters']).should.equal(10) + response['Parameters'][0]['Type'].should.equal('SecureString') + '10'.should.equal(response.get('NextToken', '')) + + +@mock_ssm +def test_describe_parameters_filter_keyid(): + client = boto3.client('ssm', region_name='us-east-1') + + for i in range(50): + p = { + 'Name': "param-%d" % i, + 'Value': "value-%d" % i, + 'Type': "String" + } + if i % 5 == 0: + p['Type'] = 'SecureString' + p['KeyId'] = "key:%d" % i + client.put_parameter(**p) + + + response = client.describe_parameters(Filters=[ + { + 'Key': 'KeyId', + 'Values': ['key:10'] + }, + ]) + len(response['Parameters']).should.equal(1) + response['Parameters'][0]['Name'].should.equal('param-10') + response['Parameters'][0]['Type'].should.equal('SecureString') + ''.should.equal(response.get('NextToken', '')) + + @mock_ssm def test_put_parameter_secure_default_kms(): client = boto3.client('ssm', region_name='us-east-1')