ebcli/operations/tagops/tagops.py (136 lines of code) (raw):
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from os import linesep
import sys
from ebcli.core import io
from ebcli.lib import elasticbeanstalk
from ebcli.resources.strings import strings
from ebcli.objects.exceptions import EBCLIException
from ebcli.objects.environment import Environment
from ebcli.operations.commonops import wait_for_success_events
from ebcli.operations.tagops.taglist import TagList, list_of_keys_of
class InvalidAttemptToModifyTagsError(EBCLIException):
""" One or more of the tags specified by the user to create
using `eb tags --add` already exist
"""
pass
def raise_validation_error(
problematic_key_set,
problem_message,
exception_class
):
"""
Method raises given a `problem_message` when an action of `action_type`
could not be performed.
:param problematic_key_set: a list of keys, the presence or absence
of which triggered the exception
:param problem_message: reason for the exception
:param exception_class: exception class to be raised when there
:return: None
"""
stringified_keys = '{0} '.format(linesep).join(sorted(problematic_key_set))
tags_error_message = '{problem_message}{linesep}{linesep} {stringified_keys}{linesep}'.format(
problem_message=problem_message,
linesep=linesep,
stringified_keys=stringified_keys
)
raise exception_class(tags_error_message)
def unicode_decode_keys(keys):
if sys.version_info < (3, 0):
return [key.decode('utf-8') for key in keys]
return keys
def validate_additions(taglist):
"""
Validates that the list of tags specified to be added do not
already exist. If one or more tags already exist, it generates a
`AttemptToCreatePreexistingTagsError` exception.
:return: None
"""
preexisting_keys = list_of_keys_of(taglist.current_list)
keys_of_tags_to_create = unicode_decode_keys(list_of_keys_of(taglist.additions))
preexisting_key_set = set(preexisting_keys).intersection(keys_of_tags_to_create)
if preexisting_key_set:
raise_validation_error(
problematic_key_set=preexisting_key_set,
problem_message=strings['tags.tag_keys_already_exist'],
exception_class=InvalidAttemptToModifyTagsError
)
def validate_deletions(taglist):
"""
Validates that the list of tags specified to be deleted currently
exist. If one or more tags do not already exist, it generates a
`InvalidAttemptToModifyTagsError` exception.
:return: None
"""
preexisting_keys = list_of_keys_of(taglist.current_list)
keys_of_tags_to_delete = unicode_decode_keys(taglist.deletions)
non_existent_key_set = list(set(keys_of_tags_to_delete) - set(preexisting_keys))
if non_existent_key_set:
raise_validation_error(
problematic_key_set=non_existent_key_set,
problem_message=strings['tags.tag_keys_dont_exist_for_deletion'],
exception_class=InvalidAttemptToModifyTagsError
)
def validate_updates(taglist):
"""
Validates that the list of tags specified to be updated currently
exist. If one or more tags do not already exist, it generates a
`InvalidAttemptToModifyTagsError` exception.
:return: None
"""
preexisting_keys = list_of_keys_of(taglist.current_list)
keys_of_tags_to_update = unicode_decode_keys(list_of_keys_of(taglist.updates))
non_existent_key_set = list(set(keys_of_tags_to_update) - set(preexisting_keys))
if non_existent_key_set:
raise_validation_error(
problematic_key_set=non_existent_key_set,
problem_message=strings['tags.tag_keys_dont_exist_for_update'],
exception_class=InvalidAttemptToModifyTagsError
)
class TagOps(object):
"""
Tag Operations class that delegates the tasks of validating syntax and uniqueness
of tags, and performing EB API calls to other classes.
"""
def __init__(self, resource_arn, verbose):
self.resource_arn = resource_arn
self.taglist = None
self.verbose = verbose
def retrieve_taglist(self):
if self.taglist is None:
self.taglist = TagList(elasticbeanstalk.list_tags_for_resource(self.resource_arn))
def list_tags(self):
self.retrieve_taglist()
if self.taglist.current_list:
self.taglist.print_tags(self.resource_arn)
def update_tags(self, resource_type=Environment):
request_id = elasticbeanstalk.update_tags_for_resource(
self.resource_arn,
self.taglist.additions + self.taglist.updates,
self.taglist.deletions
)
if resource_type == Environment:
wait_for_success_events(request_id)
if self.verbose:
self.__communicate_changes_to_stdout()
def handle_addition_string(self, addition_string):
"""
Passes on string of the form 'key=value,..." to TagList
which will maintain a list of keys specified for addition
:param addition_string: a string of the form 'key=value,...'
:return: None
"""
self.retrieve_taglist()
self.taglist.populate_add_list(addition_string)
validate_additions(self.taglist)
def handle_deletion_string(self, deletion_string):
"""
Passes on string of the form 'key,..." to TagList
which will maintain a list of keys specified for deletion
:param deletion_string: a string of the form 'key,...'
:return: None
"""
self.retrieve_taglist()
self.taglist.populate_delete_list(deletion_string)
validate_deletions(self.taglist)
def handle_update_string(self, update_string):
"""
Passes on string of the form 'key=value,..." to TagList
which will maintain a list of keys specified for update
:param update_string: a string of the form 'key=value,...'
:return: None
"""
self.retrieve_taglist()
self.taglist.populate_update_list(update_string)
validate_updates(self.taglist)
def __communicate_changes_to_stdout(self):
"""
Print changes to STDOUT.
:return: None
"""
keys_to_update = list_of_keys_of(self.taglist.updates)
additions = [
addition for addition in self.taglist.additions
if addition['Key'] not in keys_to_update
]
deletions = self.taglist.deletions
updates = self.taglist.updates
if additions:
io.echo('Added Tags:')
io.echo(
linesep.join(
[
" Key: '{0}' Value: '{1}'".format(
addition['Key'],
addition['Value']
) for addition in additions
]
)
)
io.echo('')
if deletions:
io.echo('Deleted Tags:')
io.echo(linesep.join([" Key: '{0}'".format(deletion) for deletion in deletions]))
io.echo('')
if updates:
io.echo('Updated Tags:')
io.echo(
linesep.join(
[
" Key: '{0}' Value: '{1}'".format(
update['Key'],
update['Value']
) for update in updates
]
)
)
io.echo('')
def get_and_validate_tags(addition_string):
if not addition_string:
return []
taglist = TagList([])
taglist.populate_add_list(addition_string)
validate_additions(taglist)
return taglist.additions