-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #907 from mvantellingen/ssm
Implement support for SSM parameter store
- Loading branch information
Showing
8 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from __future__ import unicode_literals | ||
from .models import ssm_backends | ||
from ..core.models import base_decorator | ||
|
||
ssm_backend = ssm_backends['us-east-1'] | ||
mock_ssm = base_decorator(ssm_backends) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from __future__ import unicode_literals | ||
|
||
from moto.core import BaseBackend, BaseModel | ||
from moto.ec2 import ec2_backends | ||
|
||
|
||
class Parameter(BaseModel): | ||
def __init__(self, name, value, type, description, keyid): | ||
self.name = name | ||
self.type = type | ||
self.description = description | ||
self.keyid = keyid | ||
|
||
if self.type == 'SecureString': | ||
self.value = self.encrypt(value) | ||
else: | ||
self.value = value | ||
|
||
def encrypt(self, value): | ||
return 'kms:{}:'.format(self.keyid or 'default') + value | ||
|
||
def decrypt(self, value): | ||
if self.type != 'SecureString': | ||
return value | ||
|
||
prefix = 'kms:{}:'.format(self.keyid or 'default') | ||
if value.startswith(prefix): | ||
return value[len(prefix):] | ||
|
||
def response_object(self, decrypt=False): | ||
return { | ||
'Name': self.name, | ||
'Type': self.type, | ||
'Value': self.decrypt(self.value) if decrypt else self.value | ||
} | ||
|
||
|
||
class SimpleSystemManagerBackend(BaseBackend): | ||
|
||
def __init__(self): | ||
self._parameters = {} | ||
|
||
def delete_parameter(self, name): | ||
try: | ||
del self._parameters[name] | ||
except KeyError: | ||
pass | ||
|
||
def get_parameters(self, names, with_decryption): | ||
result = [] | ||
for name in names: | ||
if name in self._parameters: | ||
result.append(self._parameters[name]) | ||
return result | ||
|
||
def put_parameter(self, name, description, value, type, keyid, overwrite): | ||
if not overwrite and name in self._parameters: | ||
return | ||
self._parameters[name] = Parameter( | ||
name, value, type, description, keyid) | ||
|
||
|
||
ssm_backends = {} | ||
for region, ec2_backend in ec2_backends.items(): | ||
ssm_backends[region] = SimpleSystemManagerBackend() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
from __future__ import unicode_literals | ||
import json | ||
|
||
from moto.core.responses import BaseResponse | ||
from .models import ssm_backends | ||
|
||
|
||
class SimpleSystemManagerResponse(BaseResponse): | ||
|
||
@property | ||
def ssm_backend(self): | ||
return ssm_backends[self.region] | ||
|
||
@property | ||
def request_params(self): | ||
try: | ||
return json.loads(self.body) | ||
except ValueError: | ||
return {} | ||
|
||
def _get_param(self, param, default=None): | ||
return self.request_params.get(param, default) | ||
|
||
def delete_parameter(self): | ||
name = self._get_param('Name') | ||
self.ssm_backend.delete_parameter(name) | ||
return json.dumps({}) | ||
|
||
def get_parameters(self): | ||
names = self._get_param('Names') | ||
with_decryption = self._get_param('WithDecryption') | ||
|
||
result = self.ssm_backend.get_parameters(names, with_decryption) | ||
|
||
response = { | ||
'Parameters': [], | ||
'InvalidParameters': [], | ||
} | ||
|
||
for parameter in result: | ||
param_data = parameter.response_object(with_decryption) | ||
response['Parameters'].append(param_data) | ||
|
||
return json.dumps(response) | ||
|
||
def put_parameter(self): | ||
name = self._get_param('Name') | ||
description = self._get_param('Description') | ||
value = self._get_param('Value') | ||
type_ = self._get_param('Type') | ||
keyid = self._get_param('KeyId') | ||
overwrite = self._get_param('Overwrite', False) | ||
|
||
self.ssm_backend.put_parameter( | ||
name, description, value, type_, keyid, overwrite) | ||
return json.dumps({}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from __future__ import unicode_literals | ||
from .responses import SimpleSystemManagerResponse | ||
|
||
url_bases = [ | ||
"https?://ssm.(.+).amazonaws.com", | ||
] | ||
|
||
url_paths = { | ||
'{0}/$': SimpleSystemManagerResponse.dispatch, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
from __future__ import unicode_literals | ||
|
||
import boto3 | ||
import sure # noqa | ||
|
||
from moto import mock_ssm | ||
|
||
|
||
@mock_ssm | ||
def test_delete_parameter(): | ||
client = boto3.client('ssm', region_name='us-east-1') | ||
|
||
client.put_parameter( | ||
Name='test', | ||
Description='A test parameter', | ||
Value='value', | ||
Type='String') | ||
|
||
response = client.get_parameters(Names=['test']) | ||
len(response['Parameters']).should.equal(1) | ||
|
||
client.delete_parameter(Name='test') | ||
|
||
response = client.get_parameters(Names=['test']) | ||
len(response['Parameters']).should.equal(0) | ||
|
||
|
||
@mock_ssm | ||
def test_put_parameter(): | ||
client = boto3.client('ssm', region_name='us-east-1') | ||
|
||
client.put_parameter( | ||
Name='test', | ||
Description='A test parameter', | ||
Value='value', | ||
Type='String') | ||
|
||
response = client.get_parameters( | ||
Names=[ | ||
'test' | ||
], | ||
WithDecryption=False) | ||
|
||
len(response['Parameters']).should.equal(1) | ||
response['Parameters'][0]['Name'].should.equal('test') | ||
response['Parameters'][0]['Value'].should.equal('value') | ||
response['Parameters'][0]['Type'].should.equal('String') | ||
|
||
|
||
@mock_ssm | ||
def test_put_parameter_secure_default_kms(): | ||
client = boto3.client('ssm', region_name='us-east-1') | ||
|
||
client.put_parameter( | ||
Name='test', | ||
Description='A test parameter', | ||
Value='value', | ||
Type='SecureString') | ||
|
||
response = client.get_parameters( | ||
Names=[ | ||
'test' | ||
], | ||
WithDecryption=False) | ||
|
||
len(response['Parameters']).should.equal(1) | ||
response['Parameters'][0]['Name'].should.equal('test') | ||
response['Parameters'][0]['Value'].should.equal('kms:default:value') | ||
response['Parameters'][0]['Type'].should.equal('SecureString') | ||
|
||
response = client.get_parameters( | ||
Names=[ | ||
'test' | ||
], | ||
WithDecryption=True) | ||
|
||
len(response['Parameters']).should.equal(1) | ||
response['Parameters'][0]['Name'].should.equal('test') | ||
response['Parameters'][0]['Value'].should.equal('value') | ||
response['Parameters'][0]['Type'].should.equal('SecureString') | ||
|
||
|
||
@mock_ssm | ||
def test_put_parameter_secure_custom_kms(): | ||
client = boto3.client('ssm', region_name='us-east-1') | ||
|
||
client.put_parameter( | ||
Name='test', | ||
Description='A test parameter', | ||
Value='value', | ||
Type='SecureString', | ||
KeyId='foo') | ||
|
||
response = client.get_parameters( | ||
Names=[ | ||
'test' | ||
], | ||
WithDecryption=False) | ||
|
||
len(response['Parameters']).should.equal(1) | ||
response['Parameters'][0]['Name'].should.equal('test') | ||
response['Parameters'][0]['Value'].should.equal('kms:foo:value') | ||
response['Parameters'][0]['Type'].should.equal('SecureString') | ||
|
||
response = client.get_parameters( | ||
Names=[ | ||
'test' | ||
], | ||
WithDecryption=True) | ||
|
||
len(response['Parameters']).should.equal(1) | ||
response['Parameters'][0]['Name'].should.equal('test') | ||
response['Parameters'][0]['Value'].should.equal('value') | ||
response['Parameters'][0]['Type'].should.equal('SecureString') |