Improve ecs_ecr module to handle image_tag_mutability feature (#62871)

Since 2019 jul. 26, AWS Elastic Container Registry can be configured to be immutable that prevent overwritting of an existing tag in the registry.
https://aws.amazon.com/fr/about-aws/whats-new/2019/07/amazon-ecr-now-supports-immutable-image-tags/
https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-tag-mutability.html

By default, ecr is created MUTABLE, as this was the only option before this feature.
This module leverage new capabilities of boto3 to configure image_tag_mutability option.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecr.html#ECR.Client.put_image_tag_mutability

The image_tag_mutability option definition was inspired by existing terraform module.
https://www.terraform.io/docs/providers/aws/r/ecr_repository.html
This commit is contained in:
Benjamin Brabant 2019-10-11 18:13:13 +02:00 committed by Jill R
parent df283788e5
commit 29939383e6
2 changed files with 121 additions and 8 deletions

View file

@ -5,14 +5,13 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # 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 from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: ecs_ecr module: ecs_ecr
@ -48,6 +47,13 @@ options:
required: false required: false
default: false default: false
type: bool type: bool
image_tag_mutability:
description:
- configure whether repository should be mutable (ie. an already existing tag can be overwritten) or not
required: false
choices: [mutable, immutable]
default: 'mutable'
version_added: '2.10'
state: state:
description: description:
- create or destroy the repository - create or destroy the repository
@ -97,6 +103,12 @@ EXAMPLES = '''
ecs_ecr: ecs_ecr:
name: needs-no-policy name: needs-no-policy
delete_policy: yes delete_policy: yes
- name: create immutable ecr-repo
ecs_ecr:
name: super/cool
image_tag_mutability: immutable
''' '''
RETURN = ''' RETURN = '''
@ -190,15 +202,17 @@ class EcsEcr:
return None return None
raise raise
def create_repository(self, registry_id, name): def create_repository(self, registry_id, name, image_tag_mutability):
if registry_id: if registry_id:
default_registry_id = self.sts.get_caller_identity().get('Account') default_registry_id = self.sts.get_caller_identity().get('Account')
if registry_id != default_registry_id: if registry_id != default_registry_id:
raise Exception('Cannot create repository in registry {0}.' raise Exception('Cannot create repository in registry {0}.'
'Would be created in {1} instead.'.format( 'Would be created in {1} instead.'.format(registry_id, default_registry_id))
registry_id, default_registry_id))
if not self.check_mode: if not self.check_mode:
repo = self.ecr.create_repository(repositoryName=name).get('repository') repo = self.ecr.create_repository(
repositoryName=name,
imageTagMutability=image_tag_mutability).get('repository')
self.changed = True self.changed = True
return repo return repo
else: else:
@ -250,6 +264,23 @@ class EcsEcr:
return policy return policy
return None return None
def put_image_tag_mutability(self, registry_id, name, new_mutability_configuration):
repo = self.get_repository(registry_id, name)
current_mutability_configuration = repo.get('imageTagMutability')
if current_mutability_configuration != new_mutability_configuration:
if not self.check_mode:
self.ecr.put_image_tag_mutability(
repositoryName=name,
imageTagMutability=new_mutability_configuration,
**build_kwargs(registry_id))
else:
self.skipped = True
self.changed = True
repo['imageTagMutability'] = new_mutability_configuration
return repo
def sort_lists_of_strings(policy): def sort_lists_of_strings(policy):
for statement_index in range(0, len(policy.get('Statement', []))): for statement_index in range(0, len(policy.get('Statement', []))):
@ -270,6 +301,7 @@ def run(ecr, params, verbosity):
delete_policy = params['delete_policy'] delete_policy = params['delete_policy']
registry_id = params['registry_id'] registry_id = params['registry_id']
force_set_policy = params['force_set_policy'] force_set_policy = params['force_set_policy']
image_tag_mutability = params['image_tag_mutability'].upper()
# If a policy was given, parse it # If a policy was given, parse it
policy = policy_text and json.loads(policy_text) policy = policy_text and json.loads(policy_text)
@ -281,10 +313,13 @@ def run(ecr, params, verbosity):
if state == 'present': if state == 'present':
result['created'] = False result['created'] = False
if not repo: if not repo:
repo = ecr.create_repository(registry_id, name) repo = ecr.create_repository(registry_id, name, image_tag_mutability)
result['changed'] = True result['changed'] = True
result['created'] = True result['created'] = True
else:
repo = ecr.put_image_tag_mutability(registry_id, name, image_tag_mutability)
result['repository'] = repo result['repository'] = repo
if delete_policy: if delete_policy:
@ -358,7 +393,9 @@ def main():
default='present'), default='present'),
force_set_policy=dict(required=False, type='bool', default=False), force_set_policy=dict(required=False, type='bool', default=False),
policy=dict(required=False, type='json'), policy=dict(required=False, type='json'),
delete_policy=dict(required=False, type='bool'))) delete_policy=dict(required=False, type='bool'),
image_tag_mutability=dict(required=False, choices=['mutable', 'immutable'],
default='mutable')))
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,

View file

@ -55,6 +55,11 @@
- result is changed - result is changed
- result.created - result.created
- name: it should have been configured as mutable by default
assert:
that:
- result.repository.imageTagMutability == "MUTABLE"
- name: When creating a repository that already exists in check mode - name: When creating a repository that already exists in check mode
ecs_ecr: ecs_ecr:
@ -329,6 +334,77 @@
that: that:
- result is not changed - result is not changed
- name: When creating an immutable repository
ecs_ecr:
name: '{{ ecr_name }}'
region: '{{ ec2_region }}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
image_tag_mutability: immutable
register: result
- name: it should change and create
assert:
that:
- result is changed
- result.created
- name: it should have been configured as immutable
assert:
that:
- result.repository.imageTagMutability == "IMMUTABLE"
- name: When configuring an existing immutable repository to be mutable in check mode
ecs_ecr:
name: '{{ ecr_name }}'
region: '{{ ec2_region }}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
image_tag_mutability: mutable
register: result
check_mode: yes
- name: it should skip, change and configured mutable
assert:
that:
- result is skipped
- result is changed
- result.repository.imageTagMutability == "MUTABLE"
- name: When configuring an existing immutable repository to be mutable
ecs_ecr:
name: '{{ ecr_name }}'
region: '{{ ec2_region }}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
image_tag_mutability: mutable
register: result
- name: it should change and configured mutable
assert:
that:
- result is changed
- result.repository.imageTagMutability == "MUTABLE"
- name: When configuring an already mutable repository to be mutable
ecs_ecr:
name: '{{ ecr_name }}'
region: '{{ ec2_region }}'
ec2_access_key: '{{ec2_access_key}}'
ec2_secret_key: '{{ec2_secret_key}}'
security_token: '{{security_token}}'
image_tag_mutability: mutable
register: result
- name: it should not change
assert:
that:
- result is not changed
always: always:
- name: Delete lingering ECR repository - name: Delete lingering ECR repository