You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ansible/lib/ansible/modules/cloud/amazon/ecs_ecr.py

366 lines
12 KiB
Python

#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ecs_ecr
version_added: "2.3"
short_description: Manage Elastic Container Registry repositories
description:
- Manage Elastic Container Registry repositories
requirements: [ boto3 ]
options:
name:
description:
- the name of the repository
required: true
registry_id:
description:
- AWS account id associated with the registry.
- If not specified, the default registry is assumed.
required: false
policy:
description:
- JSON or dict that represents the new policy
required: false
force_set_policy:
description:
- if no, prevents setting a policy that would prevent you from
setting another policy in the future.
required: false
default: false
delete_policy:
description:
- if yes, remove the policy from the repository
required: false
default: false
state:
description:
- create or destroy the repository
required: false
choices: [present, absent]
default: 'present'
author:
- David M. Lee (@leedm777)
extends_documentation_fragment:
- aws
- ec2
'''
EXAMPLES = '''
# If the repository does not exist, it is created. If it does exist, would not
# affect any policies already on it.
- name: ecr-repo
ecs_ecr: name=super/cool
- name: destroy-ecr-repo
ecs_ecr: name=old/busted state=absent
- name: Cross account ecr-repo
ecs_ecr: registry_id=999999999999 name=cross/account
- name: set-policy as object
ecs_ecr:
name: needs-policy-object
policy:
Version: '2008-10-17'
Statement:
- Sid: read-only
Effect: Allow
Principal:
AWS: '{{ read_only_arn }}'
Action:
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
- name: set-policy as string
ecs_ecr:
name: needs-policy-string
policy: "{{ lookup('template', 'policy.json.j2') }}"
- name: delete-policy
ecs_ecr:
name: needs-no-policy
delete_policy: yes
'''
RETURN = '''
state:
type: string
description: The asserted state of the repository (present, absent)
returned: always
created:
type: boolean
description: If true, the repository was created
returned: always
name:
type: string
description: The name of the repository
returned: "when state == 'absent'"
repository:
type: dict
description: The created or updated repository
returned: "when state == 'present'"
sample:
createdAt: '2017-01-17T08:41:32-06:00'
registryId: '999999999999'
repositoryArn: arn:aws:ecr:us-east-1:999999999999:repository/ecr-test-1484664090
repositoryName: ecr-test-1484664090
repositoryUri: 999999999999.dkr.ecr.us-east-1.amazonaws.com/ecr-test-1484664090
'''
import json
import traceback
try:
from botocore.exceptions import ClientError
except ImportError:
pass # Taken care of by ec2.HAS_BOTO3
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ec2 import (HAS_BOTO3, boto3_conn, boto_exception, ec2_argument_spec,
get_aws_connection_info, sort_json_policy_dict)
def build_kwargs(registry_id):
"""
Builds a kwargs dict which may contain the optional registryId.
:param registry_id: Optional string containing the registryId.
:return: kwargs dict with registryId, if given
"""
if not registry_id:
return dict()
else:
return dict(registryId=registry_id)
class EcsEcr:
def __init__(self, module):
region, ec2_url, aws_connect_kwargs = \
get_aws_connection_info(module, boto3=True)
self.ecr = boto3_conn(module, conn_type='client',
resource='ecr', region=region,
endpoint=ec2_url, **aws_connect_kwargs)
self.sts = boto3_conn(module, conn_type='client',
resource='sts', region=region,
endpoint=ec2_url, **aws_connect_kwargs)
self.check_mode = module.check_mode
self.changed = False
self.skipped = False
def get_repository(self, registry_id, name):
try:
res = self.ecr.describe_repositories(
repositoryNames=[name], **build_kwargs(registry_id))
repos = res.get('repositories')
return repos and repos[0]
except ClientError as err:
code = err.response['Error'].get('Code', 'Unknown')
if code == 'RepositoryNotFoundException':
return None
raise
def get_repository_policy(self, registry_id, name):
try:
res = self.ecr.get_repository_policy(
repositoryName=name, **build_kwargs(registry_id))
text = res.get('policyText')
return text and json.loads(text)
except ClientError as err:
code = err.response['Error'].get('Code', 'Unknown')
if code == 'RepositoryPolicyNotFoundException':
return None
raise
def create_repository(self, registry_id, name):
if registry_id:
default_registry_id = self.sts.get_caller_identity().get('Account')
if registry_id != default_registry_id:
raise Exception('Cannot create repository in registry {}.'
'Would be created in {} instead.'.format(
registry_id, default_registry_id))
if not self.check_mode:
repo = self.ecr.create_repository(repositoryName=name).get('repository')
self.changed = True
return repo
else:
self.skipped = True
return dict(repositoryName=name)
def set_repository_policy(self, registry_id, name, policy_text, force):
if not self.check_mode:
policy = self.ecr.set_repository_policy(
repositoryName=name,
policyText=policy_text,
force=force,
**build_kwargs(registry_id))
self.changed = True
return policy
else:
self.skipped = True
if self.get_repository(registry_id, name) is None:
printable = name
if registry_id:
printable = '{}:{}'.format(registry_id, name)
raise Exception(
'could not find repository {}'.format(printable))
return
def delete_repository(self, registry_id, name):
if not self.check_mode:
repo = self.ecr.delete_repository(
repositoryName=name, **build_kwargs(registry_id))
self.changed = True
return repo
else:
repo = self.get_repository(registry_id, name)
if repo:
self.skipped = True
return repo
return None
def delete_repository_policy(self, registry_id, name):
if not self.check_mode:
policy = self.ecr.delete_repository_policy(
repositoryName=name, **build_kwargs(registry_id))
self.changed = True
return policy
else:
policy = self.get_repository_policy(registry_id, name)
if policy:
self.skipped = True
return policy
return None
def run(ecr, params, verbosity):
# type: (EcsEcr, dict, int) -> Tuple[bool, dict]
result = {}
try:
name = params['name']
state = params['state']
policy_text = params['policy']
delete_policy = params['delete_policy']
registry_id = params['registry_id']
force_set_policy = params['force_set_policy']
# If a policy was given, parse it
policy = policy_text and json.loads(policy_text)
result['state'] = state
result['created'] = False
repo = ecr.get_repository(registry_id, name)
if state == 'present':
result['created'] = False
if not repo:
repo = ecr.create_repository(registry_id, name)
result['changed'] = True
result['created'] = True
result['repository'] = repo
if delete_policy:
original_policy = ecr.get_repository_policy(registry_id, name)
if verbosity >= 2:
result['policy'] = None
if verbosity >= 3:
result['original_policy'] = original_policy
if original_policy:
ecr.delete_repository_policy(registry_id, name)
result['changed'] = True
elif policy_text is not None:
try:
policy = sort_json_policy_dict(policy)
if verbosity >= 2:
result['policy'] = policy
original_policy = ecr.get_repository_policy(
registry_id, name)
if original_policy:
original_policy = sort_json_policy_dict(original_policy)
if verbosity >= 3:
result['original_policy'] = original_policy
if original_policy != policy:
ecr.set_repository_policy(
registry_id, name, policy_text, force_set_policy)
result['changed'] = True
except:
# Some failure w/ the policy. It's helpful to know what the
# policy is.
result['policy'] = policy_text
raise
elif state == 'absent':
result['name'] = name
if repo:
ecr.delete_repository(registry_id, name)
result['changed'] = True
except Exception as err:
msg = str(err)
if isinstance(err, ClientError):
msg = boto_exception(err)
result['msg'] = msg
result['exception'] = traceback.format_exc()
return False, result
if ecr.skipped:
result['skipped'] = True
if ecr.changed:
result['changed'] = True
return True, result
def main():
argument_spec = ec2_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
registry_id=dict(required=False),
state=dict(required=False, choices=['present', 'absent'],
default='present'),
force_set_policy=dict(required=False, type='bool', default=False),
policy=dict(required=False, type='json'),
delete_policy=dict(required=False, type='bool')))
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
mutually_exclusive=[
['policy', 'delete_policy']])
if not HAS_BOTO3:
module.fail_json(msg='boto3 required for this module')
ecr = EcsEcr(module)
passed, result = run(ecr, module.params, module._verbosity)
if passed:
module.exit_json(**result)
else:
module.fail_json(**result)
if __name__ == '__main__':
main()