From 29061ec0f83a6517492ccd856fb5305f04887f69 Mon Sep 17 00:00:00 2001 From: Chris Wolfe Date: Mon, 19 Feb 2018 09:10:52 -0600 Subject: [PATCH 1/4] add a basic test to start --- tests/test_ssm/test_ssm_boto3.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index ff8e5e8a..c9be673a 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -458,3 +458,20 @@ def test_add_remove_list_tags_for_resource(): ResourceType='Parameter' ) len(response['TagList']).should.equal(0) + + +@mock_ssm +def test_send_command(): + ssm_document = 'AWS-RunShellScript' + script = '#!/bin/bash\necho \'hello world\'' + + client = boto3.client('ssm', region_name='us-east-1') + response = client.send_command( + InstanceIds=['i-123456'], + DocumentName=ssm_document, + TimeoutSeconds=60, + Parameters={'commands': [script]}, + OutputS3BucketName='the-bucket' + ) + + assert response['Command'] From 99d336241715afd3a3438b049972e713008d527b Mon Sep 17 00:00:00 2001 From: Chris Wolfe Date: Mon, 19 Feb 2018 09:39:29 -0600 Subject: [PATCH 2/4] add code to respond to SSM send_command --- moto/ssm/models.py | 39 +++++++++++++++++++++++++++++++++++++++ moto/ssm/responses.py | 5 +++++ 2 files changed, 44 insertions(+) diff --git a/moto/ssm/models.py b/moto/ssm/models.py index d8dc10a4..c15a2047 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -5,7 +5,9 @@ from collections import defaultdict from moto.core import BaseBackend, BaseModel from moto.ec2 import ec2_backends +import datetime import time +import uuid class Parameter(BaseModel): @@ -138,6 +140,43 @@ class SimpleSystemManagerBackend(BaseBackend): def list_tags_for_resource(self, resource_type, resource_id): return self._resource_tags[resource_type][resource_id] + def send_command(self, **kwargs): + instances = kwargs['InstanceIds'] + now = datetime.datetime.now() + expires_after = now + datetime.timedelta(0, int(kwargs['TimeoutSeconds'])) + return { + 'Command': { + 'CommandId': str(uuid.uuid4()), + 'DocumentName': kwargs['DocumentName'], + 'Comment': kwargs.get('Comment'), + 'ExpiresAfter': expires_after.isoformat(), + 'Parameters': { + 'string': [ + 'string', + ] + }, + 'InstanceIds': kwargs['InstanceIds'], + 'Targets': kwargs.get('targets'), + 'RequestedDateTime': now.isoformat(), + 'Status': 'Success', + 'StatusDetails': 'string', + 'OutputS3Region': 'string', + 'OutputS3BucketName': 'string', + 'OutputS3KeyPrefix': 'string', + 'MaxConcurrency': 'string', + 'MaxErrors': 'string', + 'TargetCount': len(instances), + 'CompletedCount': len(instances), + 'ErrorCount': 0, + 'ServiceRole': kwargs.get('ServiceRoleArn'), + 'NotificationConfig': { + 'NotificationArn': 'string', + 'NotificationEvents': ['Success'], + 'NotificationType': 'Command' + } + } + } + ssm_backends = {} for region, ec2_backend in ec2_backends.items(): diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py index 0b4ca3b6..757bf031 100644 --- a/moto/ssm/responses.py +++ b/moto/ssm/responses.py @@ -190,3 +190,8 @@ class SimpleSystemManagerResponse(BaseResponse): tag_list = [{'Key': k, 'Value': v} for (k, v) in tags.items()] response = {'TagList': tag_list} return json.dumps(response) + + def send_command(self): + return json.dumps( + self.ssm_backend.send_command(**self.request_params) + ) From 8ac4ff1e99c68c751046932ea360ca2c10d8a9e7 Mon Sep 17 00:00:00 2001 From: Chris Wolfe Date: Mon, 19 Feb 2018 09:58:46 -0600 Subject: [PATCH 3/4] greater granularity --- moto/ssm/models.py | 12 ++++-------- tests/test_ssm/test_ssm_boto3.py | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/moto/ssm/models.py b/moto/ssm/models.py index c15a2047..0f75599c 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -150,19 +150,15 @@ class SimpleSystemManagerBackend(BaseBackend): 'DocumentName': kwargs['DocumentName'], 'Comment': kwargs.get('Comment'), 'ExpiresAfter': expires_after.isoformat(), - 'Parameters': { - 'string': [ - 'string', - ] - }, + 'Parameters': kwargs['Parameters'], 'InstanceIds': kwargs['InstanceIds'], 'Targets': kwargs.get('targets'), 'RequestedDateTime': now.isoformat(), 'Status': 'Success', 'StatusDetails': 'string', - 'OutputS3Region': 'string', - 'OutputS3BucketName': 'string', - 'OutputS3KeyPrefix': 'string', + 'OutputS3Region': kwargs.get('OutputS3Region'), + 'OutputS3BucketName': kwargs.get('OutputS3BucketName'), + 'OutputS3KeyPrefix': kwargs.get('OutputS3KeyPrefix'), 'MaxConcurrency': 'string', 'MaxErrors': 'string', 'TargetCount': len(instances), diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index c9be673a..5d658873 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -463,15 +463,27 @@ def test_add_remove_list_tags_for_resource(): @mock_ssm def test_send_command(): ssm_document = 'AWS-RunShellScript' - script = '#!/bin/bash\necho \'hello world\'' + params = {'commands': ['#!/bin/bash\necho \'hello world\'']} client = boto3.client('ssm', region_name='us-east-1') + before = datetime.datetime.now() response = client.send_command( InstanceIds=['i-123456'], DocumentName=ssm_document, TimeoutSeconds=60, - Parameters={'commands': [script]}, - OutputS3BucketName='the-bucket' + Parameters=params, + OutputS3Region='us-east-2', + OutputS3BucketName='the-bucket', + OutputS3KeyPrefix='pref' ) + cmd = response['Command'] - assert response['Command'] + cmd['CommandId'].should_not.be(None) + cmd['DocumentName'].should.equal(ssm_document) + cmd['Parameters'].should.equal(params) + + cmd['OutputS3Region'].should.equal('us-east-2') + cmd['OutputS3BucketName'].should.equal('the-bucket') + cmd['OutputS3KeyPrefix'].should.equal('pref') + + cmd['ExpiresAfter'].should.be.greater_than(before) From 7a6987a7f17804902ba5e470b1a93cf7bdef978d Mon Sep 17 00:00:00 2001 From: Chris Wolfe Date: Mon, 19 Feb 2018 09:59:52 -0600 Subject: [PATCH 4/4] note --- tests/test_ssm/test_ssm_boto3.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index 5d658873..0e8a770b 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -466,7 +466,9 @@ def test_send_command(): params = {'commands': ['#!/bin/bash\necho \'hello world\'']} client = boto3.client('ssm', region_name='us-east-1') + # note the timeout is determined server side, so this is a simpler check. before = datetime.datetime.now() + response = client.send_command( InstanceIds=['i-123456'], DocumentName=ssm_document,