Merge pull request #3443 from bpandola/fix-2862
Add ssm:SendCommand support for instance tag Targets
This commit is contained in:
commit
e5adfd6f12
@ -6,12 +6,11 @@ from collections import defaultdict
|
|||||||
|
|
||||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
||||||
from moto.core.exceptions import RESTError
|
from moto.core.exceptions import RESTError
|
||||||
from moto.cloudformation import cloudformation_backends
|
from moto.ec2 import ec2_backends
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import itertools
|
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -246,9 +245,6 @@ class Command(BaseModel):
|
|||||||
if targets is None:
|
if targets is None:
|
||||||
targets = []
|
targets = []
|
||||||
|
|
||||||
self.error_count = 0
|
|
||||||
self.completed_count = len(instance_ids)
|
|
||||||
self.target_count = len(instance_ids)
|
|
||||||
self.command_id = str(uuid.uuid4())
|
self.command_id = str(uuid.uuid4())
|
||||||
self.status = "Success"
|
self.status = "Success"
|
||||||
self.status_details = "Details placeholder"
|
self.status_details = "Details placeholder"
|
||||||
@ -262,7 +258,6 @@ class Command(BaseModel):
|
|||||||
|
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
self.document_name = document_name
|
self.document_name = document_name
|
||||||
self.instance_ids = instance_ids
|
|
||||||
self.max_concurrency = max_concurrency
|
self.max_concurrency = max_concurrency
|
||||||
self.max_errors = max_errors
|
self.max_errors = max_errors
|
||||||
self.notification_config = notification_config
|
self.notification_config = notification_config
|
||||||
@ -274,14 +269,19 @@ class Command(BaseModel):
|
|||||||
self.targets = targets
|
self.targets = targets
|
||||||
self.backend_region = backend_region
|
self.backend_region = backend_region
|
||||||
|
|
||||||
# Get instance ids from a cloud formation stack target.
|
self.instance_ids = instance_ids
|
||||||
stack_instance_ids = [
|
self.instance_ids += self._get_instance_ids_from_targets()
|
||||||
self.get_instance_ids_by_stack_ids(target["Values"])
|
# Ensure no duplicate instance_ids
|
||||||
for target in self.targets
|
self.instance_ids = list(set(self.instance_ids))
|
||||||
if target["Key"] == "tag:aws:cloudformation:stack-name"
|
|
||||||
]
|
|
||||||
|
|
||||||
self.instance_ids += list(itertools.chain.from_iterable(stack_instance_ids))
|
# NOTE: All of these counts are 0 in the ssm:SendCommand response
|
||||||
|
# received from a real AWS backend. The counts are correct when
|
||||||
|
# making subsequent calls to ssm:DescribeCommand or ssm:ListCommands.
|
||||||
|
# Not likely to cause any problems, but perhaps an area for future
|
||||||
|
# improvement.
|
||||||
|
self.error_count = 0
|
||||||
|
self.completed_count = len(instance_ids)
|
||||||
|
self.target_count = len(instance_ids)
|
||||||
|
|
||||||
# Create invocations with a single run command plugin.
|
# Create invocations with a single run command plugin.
|
||||||
self.invocations = []
|
self.invocations = []
|
||||||
@ -290,19 +290,15 @@ class Command(BaseModel):
|
|||||||
self.invocation_response(instance_id, "aws:runShellScript")
|
self.invocation_response(instance_id, "aws:runShellScript")
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_instance_ids_by_stack_ids(self, stack_ids):
|
def _get_instance_ids_from_targets(self):
|
||||||
instance_ids = []
|
target_instance_ids = []
|
||||||
cloudformation_backend = cloudformation_backends[self.backend_region]
|
ec2_backend = ec2_backends[self.backend_region]
|
||||||
for stack_id in stack_ids:
|
ec2_filters = {target["Key"]: target["Values"] for target in self.targets}
|
||||||
stack_resources = cloudformation_backend.list_stack_resources(stack_id)
|
reservations = ec2_backend.all_reservations(filters=ec2_filters)
|
||||||
instance_resources = [
|
for reservation in reservations:
|
||||||
instance.id
|
for instance in reservation.instances:
|
||||||
for instance in stack_resources
|
target_instance_ids.append(instance.id)
|
||||||
if instance.type == "AWS::EC2::Instance"
|
return target_instance_ids
|
||||||
]
|
|
||||||
instance_ids.extend(instance_resources)
|
|
||||||
|
|
||||||
return instance_ids
|
|
||||||
|
|
||||||
def response_object(self):
|
def response_object(self):
|
||||||
r = {
|
r = {
|
||||||
|
@ -11,7 +11,7 @@ import uuid
|
|||||||
from botocore.exceptions import ClientError, ParamValidationError
|
from botocore.exceptions import ClientError, ParamValidationError
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
from moto import mock_ssm
|
from moto import mock_ec2, mock_ssm
|
||||||
|
|
||||||
|
|
||||||
@mock_ssm
|
@mock_ssm
|
||||||
@ -1713,3 +1713,36 @@ def test_get_command_invocation():
|
|||||||
invocation_response = client.get_command_invocation(
|
invocation_response = client.get_command_invocation(
|
||||||
CommandId=cmd_id, InstanceId=instance_id, PluginName="FAKE"
|
CommandId=cmd_id, InstanceId=instance_id, PluginName="FAKE"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_ssm
|
||||||
|
def test_get_command_invocations_by_instance_tag():
|
||||||
|
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||||
|
ssm = boto3.client("ssm", region_name="us-east-1")
|
||||||
|
tag_specifications = [
|
||||||
|
{"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test-tag"}]}
|
||||||
|
]
|
||||||
|
num_instances = 3
|
||||||
|
resp = ec2.run_instances(
|
||||||
|
ImageId="ami-1234abcd",
|
||||||
|
MaxCount=num_instances,
|
||||||
|
MinCount=num_instances,
|
||||||
|
TagSpecifications=tag_specifications,
|
||||||
|
)
|
||||||
|
instance_ids = []
|
||||||
|
for instance in resp["Instances"]:
|
||||||
|
instance_ids.append(instance["InstanceId"])
|
||||||
|
instance_ids.should.have.length_of(num_instances)
|
||||||
|
|
||||||
|
command_id = ssm.send_command(
|
||||||
|
DocumentName="AWS-RunShellScript",
|
||||||
|
Targets=[{"Key": "tag:Name", "Values": ["test-tag"]}],
|
||||||
|
)["Command"]["CommandId"]
|
||||||
|
|
||||||
|
resp = ssm.list_commands(CommandId=command_id)
|
||||||
|
resp["Commands"][0]["TargetCount"].should.equal(num_instances)
|
||||||
|
|
||||||
|
for instance_id in instance_ids:
|
||||||
|
resp = ssm.get_command_invocation(CommandId=command_id, InstanceId=instance_id)
|
||||||
|
resp["Status"].should.equal("Success")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user