{"action":"created","comment":{"url":"https://api.github.com/repos/ansible/ansible/pulls/comments/128083865","pull_request_review_id":50730488,"id":128083865,"diff_hunk":"@@ -0,0 +1,311 @@\n+#!/usr/bin/python\n+# This file is part of Ansible\n+#\n+# Ansible is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# Ansible is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n+\n+ANSIBLE_METADATA = {'status': ['preview'],\n+                    'supported_by': 'community',\n+                    'metadata_version': '1.0'}\n+\n+DOCUMENTATION = '''\n+---\n+module: ecs_attribute\n+short_description: manage ecs attributes\n+description:\n+    - Create, update or delete ECS container instance attributes.\n+version_added: \"2.4\"\n+author: Andrej Svenke (@anryko)\n+requirements: [ botocore, boto3 ]\n+options:\n+    cluster:\n+        description:\n+            - The short name or full Amazon Resource Name (ARN) of the cluster\n+              that contains the resource to apply attributes.\n+        required: true\n+    state:\n+        description:\n+            - The desired state of the attributes.\n+        required: false\n+        default: present\n+        choices: ['present', 'absent']\n+    attributes:\n+        description:\n+            - List of attributes.\n+        required: true\n+        suboptions:\n+            name:\n+                description:\n+                    - The name of the attribute. Up to 128 letters (uppercase and lowercase),\n+                      numbers, hyphens, underscores, and periods are allowed.\n+                required: true\n+            value:\n+                description:\n+                    - The value of the attribute. Up to 128 letters (uppercase and lowercase),\n+                      numbers, hyphens, underscores, periods, at signs (@), forward slashes, colons,\n+                      and spaces are allowed.\n+                required: false\n+    ec2_instance_id:\n+        description:\n+            - EC2 instance ID of ECS cluster container instance.\n+        required: true\n+extends_documentation_fragment:\n+    - aws\n+    - ec2\n+'''\n+\n+EXAMPLES = '''\n+# Note: These examples do not set authentication details, see the AWS Guide for details.\n+\n+# Set attributes\n+- ecs_attribute:\n+    state: present\n+    cluster: test-cluster\n+    ec2_instance_id: \"{{ ec2_id }}\"\n+    attributes:\n+      - flavor: test\n+      - migrated\n+  delegate_to: localhost\n+\n+# Delete attributes\n+- ecs_attribute:\n+    state: absent\n+    cluster: test-cluster\n+    ec2_instance_id: \"{{ ec2_id }}\"\n+    attributes:\n+      - flavor: test\n+      - migrated\n+  delegate_to: localhost\n+'''\n+\n+RETURN = '''\n+attributes:\n+    description: attributes\n+    type: complex\n+    returned: always\n+    contains:\n+        cluster:\n+            description: cluster name\n+            type: string\n+        ec2_instance_id:\n+            description: ec2 instance id of ecs container instance\n+            type: string\n+        attributes:\n+            description: list of attributes\n+            type: list of complex\n+            contains:\n+                name:\n+                    description: name of the attribute\n+                    type: string\n+                value:\n+                    description: value of the attribute\n+                    returned: if present\n+                    type: string\n+'''\n+\n+try:\n+    import boto3\n+    from botocore.exceptions import ClientError, EndpointConnectionError\n+    HAS_BOTO3 = True\n+except ImportError:\n+    HAS_BOTO3 = False\n+\n+from ansible.module_utils.basic import AnsibleModule\n+from ansible.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info\n+\n+\n+class EcsAttributes:\n+    \"\"\"Handles ECS Cluster Attribute\"\"\"\n+\n+    def __init__(self, module, attributes):\n+        self.module = module\n+        self.attributes = attributes if self._validate_attrs(attributes) else self._parse_attrs(attributes)\n+\n+    def __bool__(self):\n+        return bool(self.attributes)\n+\n+    __nonzero__ = __bool__\n+\n+    def __iter__(self):\n+        return iter(self.attributes)\n+\n+    @staticmethod\n+    def _validate_attrs(attrs):\n+        return all(tuple(attr.keys()) in (('name', 'value'), ('value', 'name')) for attr in attrs)\n+\n+    def _parse_attrs(self, attrs):\n+        attrs_parsed = []\n+        for attr in attrs:\n+            if isinstance(attr, dict):\n+                if len(attr) != 1:\n+                    self.module.fail_json(msg=\"Incorrect attribute format - %s\" % str(attr))\n+                name, value = list(attr.items())[0]\n+                attrs_parsed.append({'name': name, 'value': value})\n+            elif isinstance(attr, str):\n+                attrs_parsed.append({'name': attr, 'value': None})\n+            else:\n+                self.module.fail_json(msg=\"Incorrect attributes format - %s\" % str(attrs))\n+\n+        return attrs_parsed\n+\n+    def _setup_attr_obj(self, ecs_arn, name, value=None, skip_value=False):\n+        attr_obj = {'targetType': 'container-instance',\n+                    'targetId': ecs_arn,\n+                    'name': name}\n+        if not skip_value and value is not None:\n+            attr_obj['value'] = value\n+\n+        return attr_obj\n+\n+    def get_for_ecs_arn(self, ecs_arn, skip_value=False):\n+        \"\"\"\n+        Returns list of attribute dicts ready to be passed to boto3\n+        attributes put/delete methods.\n+        \"\"\"\n+        return [self._setup_attr_obj(ecs_arn, skip_value=skip_value, **attr) for attr in self.attributes]\n+\n+    def diff(self, attrs):\n+        \"\"\"\n+        Returns EcsAttributes Object containing attributes which are present\n+        in self but are absent in passed attrs (EcsAttributes Object).\n+        \"\"\"\n+        attrs_diff = [attr for attr in self.attributes if attr not in attrs]\n+        return EcsAttributes(self.module, attrs_diff)\n+\n+\n+class Ec2EcsInstance:","path":"lib/ansible/modules/cloud/amazon/ecs_attribute.py","position":186,"original_position":186,"commit_id":"5de44a6335b0eb2738d61b119d1ce21e0ce676b6","original_commit_id":"5de44a6335b0eb2738d61b119d1ce21e0ce676b6","user":{"login":"ryansb","id":636610,"avatar_url":"https://avatars1.githubusercontent.com/u/636610?v=4","gravatar_id":"","url":"https://api.github.com/users/ryansb","html_url":"https://github.com/ryansb","followers_url":"https://api.github.com/users/ryansb/followers","following_url":"https://api.github.com/users/ryansb/following{/other_user}","gists_url":"https://api.github.com/users/ryansb/gists{/gist_id}","starred_url":"https://api.github.com/users/ryansb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryansb/subscriptions","organizations_url":"https://api.github.com/users/ryansb/orgs","repos_url":"https://api.github.com/users/ryansb/repos","events_url":"https://api.github.com/users/ryansb/events{/privacy}","received_events_url":"https://api.github.com/users/ryansb/received_events","type":"User","site_admin":false},"body":"Change to `class Ec2EcsInstance(object):`","created_at":"2017-07-18T20:18:47Z","updated_at":"2017-07-18T20:19:43Z","html_url":"https://github.com/ansible/ansible/pull/20618#discussion_r128083865","pull_request_url":"https://api.github.com/repos/ansible/ansible/pulls/20618","_links":{"self":{"href":"https://api.github.com/repos/ansible/ansible/pulls/comments/128083865"},"html":{"href":"https://github.com/ansible/ansible/pull/20618#discussion_r128083865"},"pull_request":{"href":"https://api.github.com/repos/ansible/ansible/pulls/20618"}}},"pull_request":{"url":"https://api.github.com/repos/ansible/ansible/pulls/20618","id":103035438,"html_url":"https://github.com/ansible/ansible/pull/20618","diff_url":"https://github.com/ansible/ansible/pull/20618.diff","patch_url":"https://github.com/ansible/ansible/pull/20618.patch","issue_url":"https://api.github.com/repos/ansible/ansible/issues/20618","number":20618,"state":"open","locked":false,"title":"Add ecs_attribute module.","user":{"login":"anryko","id":3940391,"avatar_url":"https://avatars1.githubusercontent.com/u/3940391?v=4","gravatar_id":"","url":"https://api.github.com/users/anryko","html_url":"https://github.com/anryko","followers_url":"https://api.github.com/users/anryko/followers","following_url":"https://api.github.com/users/anryko/following{/other_user}","gists_url":"https://api.github.com/users/anryko/gists{/gist_id}","starred_url":"https://api.github.com/users/anryko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anryko/subscriptions","organizations_url":"https://api.github.com/users/anryko/orgs","repos_url":"https://api.github.com/users/anryko/repos","events_url":"https://api.github.com/users/anryko/events{/privacy}","received_events_url":"https://api.github.com/users/anryko/received_events","type":"User","site_admin":false},"body":"##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest: -->\r\n - New Module Pull Request\r\n\r\n##### COMPONENT NAME\r\n<!--- Name of the module/plugin/module/task -->\r\necs_attribute\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from “ansible --version” between quotes below -->\r\n```\r\nansible 2.3.0 (ecs_attribute 0088c74894) last updated 2017/01/25 01:21:30 (GMT +200)\r\n```\r\n\r\n##### SUMMARY\r\n<!--- Describe the change, including rationale and design decisions -->\r\nIn ecs_service (https://github.com/ansible/ansible/pull/20503) I added functionality to constrain container placement based on attributes (as one of placement_constraints expression options). This module will provide possibility to assign attributes to ecs container instances based on ec2 instance id.\r\n<!---\r\nIf you are fixing an existing issue, please include \"Fixes #nnn\" in your\r\ncommit message and your description; but you should still explain what\r\nthe change does.\r\n-->\r\n\r\n<!-- Paste verbatim command output below, e.g. before and after your change -->\r\n```\r\n# Set attribute\r\n- ecs_attribute:\r\n    cluster: test-cluster\r\n    name: flavor\r\n    value: test\r\n    state: present\r\n    ec2_instance_id: \"{{ ec2_id }}\"\r\n  delegate_to: localhost\r\n\r\n# Delete attribute\r\n- ecs_attribute:\r\n    cluster: test-cluster\r\n    name: flavor\r\n    state: absent\r\n    ec2_instance_id: \"{{ ec2_id }}\"\r\n  delegate_to: localhost\r\n```\r\n","created_at":"2017-01-25T00:47:59Z","updated_at":"2017-07-18T20:19:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f9b4a201c5a5563b4670cec6e27d07ee3623653a","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/ansible/ansible/pulls/20618/commits","review_comments_url":"https://api.github.com/repos/ansible/ansible/pulls/20618/comments","review_comment_url":"https://api.github.com/repos/ansible/ansible/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ansible/ansible/issues/20618/comments","statuses_url":"https://api.github.com/repos/ansible/ansible/statuses/5de44a6335b0eb2738d61b119d1ce21e0ce676b6","head":{"label":"anryko:ecs_attribute","ref":"ecs_attribute","sha":"5de44a6335b0eb2738d61b119d1ce21e0ce676b6","user":{"login":"anryko","id":3940391,"avatar_url":"https://avatars1.githubusercontent.com/u/3940391?v=4","gravatar_id":"","url":"https://api.github.com/users/anryko","html_url":"https://github.com/anryko","followers_url":"https://api.github.com/users/anryko/followers","following_url":"https://api.github.com/users/anryko/following{/other_user}","gists_url":"https://api.github.com/users/anryko/gists{/gist_id}","starred_url":"https://api.github.com/users/anryko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anryko/subscriptions","organizations_url":"https://api.github.com/users/anryko/orgs","repos_url":"https://api.github.com/users/anryko/repos","events_url":"https://api.github.com/users/anryko/events{/privacy}","received_events_url":"https://api.github.com/users/anryko/received_events","type":"User","site_admin":false},"repo":{"id":79469519,"name":"ansible","full_name":"anryko/ansible","owner":{"login":"anryko","id":3940391,"avatar_url":"https://avatars1.githubusercontent.com/u/3940391?v=4","gravatar_id":"","url":"https://api.github.com/users/anryko","html_url":"https://github.com/anryko","followers_url":"https://api.github.com/users/anryko/followers","following_url":"https://api.github.com/users/anryko/following{/other_user}","gists_url":"https://api.github.com/users/anryko/gists{/gist_id}","starred_url":"https://api.github.com/users/anryko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anryko/subscriptions","organizations_url":"https://api.github.com/users/anryko/orgs","repos_url":"https://api.github.com/users/anryko/repos","events_url":"https://api.github.com/users/anryko/events{/privacy}","received_events_url":"https://api.github.com/users/anryko/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/anryko/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.","fork":true,"url":"https://api.github.com/repos/anryko/ansible","forks_url":"https://api.github.com/repos/anryko/ansible/forks","keys_url":"https://api.github.com/repos/anryko/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/anryko/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/anryko/ansible/teams","hooks_url":"https://api.github.com/repos/anryko/ansible/hooks","issue_events_url":"https://api.github.com/repos/anryko/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/anryko/ansible/events","assignees_url":"https://api.github.com/repos/anryko/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/anryko/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/anryko/ansible/tags","blobs_url":"https://api.github.com/repos/anryko/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/anryko/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/anryko/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/anryko/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/anryko/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/anryko/ansible/languages","stargazers_url":"https://api.github.com/repos/anryko/ansible/stargazers","contributors_url":"https://api.github.com/repos/anryko/ansible/contributors","subscribers_url":"https://api.github.com/repos/anryko/ansible/subscribers","subscription_url":"https://api.github.com/repos/anryko/ansible/subscription","commits_url":"https://api.github.com/repos/anryko/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/anryko/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/anryko/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/anryko/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/anryko/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/anryko/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/anryko/ansible/merges","archive_url":"https://api.github.com/repos/anryko/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/anryko/ansible/downloads","issues_url":"https://api.github.com/repos/anryko/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/anryko/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/anryko/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/anryko/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/anryko/ansible/labels{/name}","releases_url":"https://api.github.com/repos/anryko/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/anryko/ansible/deployments","created_at":"2017-01-19T15:59:28Z","updated_at":"2017-01-19T15:59:46Z","pushed_at":"2017-07-16T18:32:32Z","git_url":"git://github.com/anryko/ansible.git","ssh_url":"git@github.com:anryko/ansible.git","clone_url":"https://github.com/anryko/ansible.git","svn_url":"https://github.com/anryko/ansible","homepage":"http://ansible.com/","size":81100,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"devel"}},"base":{"label":"ansible:devel","ref":"devel","sha":"e228c7d0211513e64a65087a4408250a53fe94ff","user":{"login":"ansible","id":1507452,"avatar_url":"https://avatars2.githubusercontent.com/u/1507452?v=4","gravatar_id":"","url":"https://api.github.com/users/ansible","html_url":"https://github.com/ansible","followers_url":"https://api.github.com/users/ansible/followers","following_url":"https://api.github.com/users/ansible/following{/other_user}","gists_url":"https://api.github.com/users/ansible/gists{/gist_id}","starred_url":"https://api.github.com/users/ansible/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ansible/subscriptions","organizations_url":"https://api.github.com/users/ansible/orgs","repos_url":"https://api.github.com/users/ansible/repos","events_url":"https://api.github.com/users/ansible/events{/privacy}","received_events_url":"https://api.github.com/users/ansible/received_events","type":"Organization","site_admin":false},"repo":{"id":3638964,"name":"ansible","full_name":"ansible/ansible","owner":{"login":"ansible","id":1507452,"avatar_url":"https://avatars2.githubusercontent.com/u/1507452?v=4","gravatar_id":"","url":"https://api.github.com/users/ansible","html_url":"https://github.com/ansible","followers_url":"https://api.github.com/users/ansible/followers","following_url":"https://api.github.com/users/ansible/following{/other_user}","gists_url":"https://api.github.com/users/ansible/gists{/gist_id}","starred_url":"https://api.github.com/users/ansible/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ansible/subscriptions","organizations_url":"https://api.github.com/users/ansible/orgs","repos_url":"https://api.github.com/users/ansible/repos","events_url":"https://api.github.com/users/ansible/events{/privacy}","received_events_url":"https://api.github.com/users/ansible/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ansible/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.","fork":false,"url":"https://api.github.com/repos/ansible/ansible","forks_url":"https://api.github.com/repos/ansible/ansible/forks","keys_url":"https://api.github.com/repos/ansible/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ansible/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ansible/ansible/teams","hooks_url":"https://api.github.com/repos/ansible/ansible/hooks","issue_events_url":"https://api.github.com/repos/ansible/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/ansible/ansible/events","assignees_url":"https://api.github.com/repos/ansible/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/ansible/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/ansible/ansible/tags","blobs_url":"https://api.github.com/repos/ansible/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ansible/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ansible/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/ansible/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ansible/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/ansible/ansible/languages","stargazers_url":"https://api.github.com/repos/ansible/ansible/stargazers","contributors_url":"https://api.github.com/repos/ansible/ansible/contributors","subscribers_url":"https://api.github.com/repos/ansible/ansible/subscribers","subscription_url":"https://api.github.com/repos/ansible/ansible/subscription","commits_url":"https://api.github.com/repos/ansible/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/ansible/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/ansible/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/ansible/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/ansible/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/ansible/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ansible/ansible/merges","archive_url":"https://api.github.com/repos/ansible/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ansible/ansible/downloads","issues_url":"https://api.github.com/repos/ansible/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/ansible/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/ansible/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/ansible/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ansible/ansible/labels{/name}","releases_url":"https://api.github.com/repos/ansible/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/ansible/ansible/deployments","created_at":"2012-03-06T14:58:02Z","updated_at":"2017-07-18T19:01:38Z","pushed_at":"2017-07-18T20:14:09Z","git_url":"git://github.com/ansible/ansible.git","ssh_url":"git@github.com:ansible/ansible.git","clone_url":"https://github.com/ansible/ansible.git","svn_url":"https://github.com/ansible/ansible","homepage":"http://ansible.com/","size":83006,"stargazers_count":24281,"watchers_count":24281,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":8343,"mirror_url":null,"open_issues_count":3588,"forks":8343,"open_issues":3588,"watchers":24281,"default_branch":"devel"}},"_links":{"self":{"href":"https://api.github.com/repos/ansible/ansible/pulls/20618"},"html":{"href":"https://github.com/ansible/ansible/pull/20618"},"issue":{"href":"https://api.github.com/repos/ansible/ansible/issues/20618"},"comments":{"href":"https://api.github.com/repos/ansible/ansible/issues/20618/comments"},"review_comments":{"href":"https://api.github.com/repos/ansible/ansible/pulls/20618/comments"},"review_comment":{"href":"https://api.github.com/repos/ansible/ansible/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ansible/ansible/pulls/20618/commits"},"statuses":{"href":"https://api.github.com/repos/ansible/ansible/statuses/5de44a6335b0eb2738d61b119d1ce21e0ce676b6"}}}}
{"actor":{"display_login":"ryansb"}}