def test_is_boto3_error_message_single__pass__botocore(self):
     passed_exception = self._make_botocore_exception()
     returned_exception = is_boto3_error_message(
         'is not authorized to perform', e=passed_exception)
     self.assertFalse(isinstance(passed_exception, returned_exception))
     self.assertFalse(
         issubclass(returned_exception, botocore.exceptions.ClientError))
     self.assertFalse(
         issubclass(returned_exception, botocore.exceptions.BotoCoreError))
     self.assertTrue(issubclass(returned_exception, Exception))
     self.assertEqual(returned_exception.__name__,
                      "NeverEverRaisedException")
Exemplo n.º 2
0
 def create_invalidation(self, distribution_id, invalidation_batch):
     current_invalidation_response = self.get_invalidation(distribution_id, invalidation_batch['CallerReference'])
     try:
         response = self.client.create_invalidation(DistributionId=distribution_id, InvalidationBatch=invalidation_batch)
         response.pop('ResponseMetadata', None)
         if current_invalidation_response:
             return response, False
         else:
             return response, True
     except is_boto3_error_message('Your request contains a caller reference that was used for a previous invalidation '
                                   'batch for the same distribution.'):
         self.module.warn("InvalidationBatch target paths are not modifiable. "
                          "To make a new invalidation please update caller_reference.")
         return current_invalidation_response, False
     except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:  # pylint: disable=duplicate-except
         self.module.fail_json_aws(e, msg="Error creating CloudFront invalidations.")
Exemplo n.º 3
0
def create_lifecycle_rule(client, module):

    name = module.params.get("name")
    wait = module.params.get("wait")
    changed = False

    old_lifecycle_rules = fetch_rules(client, module, name)
    new_rule = build_rule(client, module)
    (changed, lifecycle_configuration) = compare_and_update_configuration(
        client, module, old_lifecycle_rules, new_rule)

    # Write lifecycle to bucket
    try:
        client.put_bucket_lifecycle_configuration(
            aws_retry=True,
            Bucket=name,
            LifecycleConfiguration=lifecycle_configuration)
    except is_boto3_error_message(
            'At least one action needs to be specified in a rule'):
        # Amazon interpretted this as not changing anything
        changed = False
    except (botocore.exceptions.ClientError,
            botocore.exceptions.BotoCoreError) as e:  # pylint: disable=duplicate-except
        module.fail_json_aws(e,
                             lifecycle_configuration=lifecycle_configuration,
                             name=name,
                             old_lifecycle_rules=old_lifecycle_rules)

    _changed = changed
    _retries = 10
    while wait and _changed and _retries:
        # We've seen examples where get_bucket_lifecycle_configuration returns
        # the updated rules, then the old rules, then the updated rules again,
        time.sleep(5)
        _retries -= 1
        new_rules = fetch_rules(client, module, name)
        (_changed, lifecycle_configuration) = compare_and_update_configuration(
            client, module, new_rules, new_rule)

    new_rules = fetch_rules(client, module, name)

    module.exit_json(changed=changed,
                     new_rule=new_rule,
                     rules=new_rules,
                     old_rules=old_lifecycle_rules,
                     _retries=_retries,
                     _config=lifecycle_configuration)
 def test_is_boto3_error_message_single__raise__client(self):
     caught_exception = None
     thrown_exception = self._make_denied_exception()
     # Test that we don't catch BotoCoreError
     try:
         raise thrown_exception
     except is_boto3_error_message('is not authorized to perform') as e:
         caught_exception = e
         caught = 'Message'
     except botocore.exceptions.ClientError as e:  # pylint: disable=duplicate-except
         caught_exception = e
         caught = 'ClientError'
     except botocore.exceptions.BotoCoreError as e:
         caught_exception = e
         caught = 'BotoCoreError'
     except Exception as e:
         caught_exception = e
         caught = 'Exception'
     self.assertEqual(caught_exception, thrown_exception)
     self.assertEqual(caught, 'Message')
def main():
    argument_spec = dict(
        app_name=dict(aliases=['name'], type='str', required=False),
        description=dict(),
        state=dict(choices=['present', 'absent'], default='present'),
        terminate_by_force=dict(type='bool', default=False, required=False)
    )

    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)

    app_name = module.params['app_name']
    description = module.params['description']
    state = module.params['state']
    terminate_by_force = module.params['terminate_by_force']

    if app_name is None:
        module.fail_json(msg='Module parameter "app_name" is required')

    result = {}

    ebs = module.client('elasticbeanstalk')

    app = describe_app(ebs, app_name, module)

    if module.check_mode:
        check_app(ebs, app, module)
        module.fail_json(msg='ASSERTION FAILURE: check_app() should not return control.')

    if state == 'present':
        if app is None:
            try:
                create_app = ebs.create_application(**filter_empty(ApplicationName=app_name,
                                                    Description=description))
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Could not create application")

            app = describe_app(ebs, app_name, module)

            result = dict(changed=True, app=app)
        else:
            if app.get("Description", None) != description:
                try:
                    if not description:
                        ebs.update_application(ApplicationName=app_name)
                    else:
                        ebs.update_application(ApplicationName=app_name, Description=description)
                except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, msg="Could not update application")

                app = describe_app(ebs, app_name, module)

                result = dict(changed=True, app=app)
            else:
                result = dict(changed=False, app=app)

    else:
        if app is None:
            result = dict(changed=False, output='Application not found', app={})
        else:
            try:
                if terminate_by_force:
                    # Running environments will be terminated before deleting the application
                    ebs.delete_application(ApplicationName=app_name, TerminateEnvByForce=terminate_by_force)
                else:
                    ebs.delete_application(ApplicationName=app_name)
                changed = True
            except is_boto3_error_message('It is currently pending deletion'):
                changed = False
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:  # pylint: disable=duplicate-except
                module.fail_json_aws(e, msg="Cannot terminate app")

            result = dict(changed=changed, app=app)

    module.exit_json(**result)
Exemplo n.º 6
0
def main():
    argument_spec = dict(
        state=dict(type='str',
                   required=True,
                   choices=['absent', 'create', 'delete', 'get', 'present'],
                   aliases=['command']),
        zone=dict(type='str'),
        hosted_zone_id=dict(type='str'),
        record=dict(type='str', required=True),
        ttl=dict(type='int', default=3600),
        type=dict(type='str',
                  required=True,
                  choices=[
                      'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA',
                      'SPF', 'SRV', 'TXT'
                  ]),
        alias=dict(type='bool'),
        alias_hosted_zone_id=dict(type='str'),
        alias_evaluate_target_health=dict(type='bool', default=False),
        value=dict(type='list', elements='str'),
        overwrite=dict(type='bool'),
        retry_interval=dict(type='int', default=500),
        private_zone=dict(type='bool', default=False),
        identifier=dict(type='str'),
        weight=dict(type='int'),
        region=dict(type='str'),
        health_check=dict(type='str'),
        failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']),
        vpc_id=dict(type='str'),
        wait=dict(type='bool', default=False),
        wait_timeout=dict(type='int', default=300),
    )

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=[['zone', 'hosted_zone_id']],
        # If alias is True then you must specify alias_hosted_zone as well
        required_together=[['alias', 'alias_hosted_zone_id']],
        # state=present, absent, create, delete THEN value is required
        required_if=(
            ('state', 'present', ['value']),
            ('state', 'create', ['value']),
            ('state', 'absent', ['value']),
            ('state', 'delete', ['value']),
        ),
        # failover, region and weight are mutually exclusive
        mutually_exclusive=[
            ('failover', 'region', 'weight'),
            ('alias', 'ttl'),
        ],
        # failover, region and weight require identifier
        required_by=dict(
            failover=('identifier', ),
            region=('identifier', ),
            weight=('identifier', ),
        ),
    )

    if module.params['state'] in ('present', 'create'):
        command_in = 'create'
    elif module.params['state'] in ('absent', 'delete'):
        command_in = 'delete'
    elif module.params['state'] == 'get':
        command_in = 'get'

    zone_in = (module.params.get('zone') or '').lower()
    hosted_zone_id_in = module.params.get('hosted_zone_id')
    ttl_in = module.params.get('ttl')
    record_in = module.params.get('record').lower()
    type_in = module.params.get('type')
    value_in = module.params.get('value') or []
    alias_in = module.params.get('alias')
    alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id')
    alias_evaluate_target_health_in = module.params.get(
        'alias_evaluate_target_health')
    retry_interval_in = module.params.get('retry_interval')

    if module.params['vpc_id'] is not None:
        private_zone_in = True
    else:
        private_zone_in = module.params.get('private_zone')

    identifier_in = module.params.get('identifier')
    weight_in = module.params.get('weight')
    region_in = module.params.get('region')
    health_check_in = module.params.get('health_check')
    failover_in = module.params.get('failover')
    vpc_id_in = module.params.get('vpc_id')
    wait_in = module.params.get('wait')
    wait_timeout_in = module.params.get('wait_timeout')

    if zone_in[-1:] != '.':
        zone_in += "."

    if record_in[-1:] != '.':
        record_in += "."

    if command_in == 'create' or command_in == 'delete':
        if alias_in and len(value_in) != 1:
            module.fail_json(
                msg=
                "parameter 'value' must contain a single dns name for alias records"
            )
        if (weight_in is None and region_in is None
                and failover_in is None) and identifier_in is not None:
            module.fail_json(
                msg=
                "You have specified identifier which makes sense only if you specify one of: weight, region or failover."
            )

    retry_decorator = AWSRetry.jittered_backoff(
        retries=MAX_AWS_RETRIES,
        delay=retry_interval_in,
        catch_extra_error_codes=['PriorRequestNotComplete'],
        max_delay=max(60, retry_interval_in),
    )

    # connect to the route53 endpoint
    try:
        route53 = module.client('route53', retry_decorator=retry_decorator)
    except botocore.exceptions.HTTPClientError as e:
        module.fail_json_aws(e, msg='Failed to connect to AWS')

    # Find the named zone ID
    zone_id = hosted_zone_id_in or get_zone_id_by_name(
        route53, module, zone_in, private_zone_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone_id is None:
        errmsg = "Zone %s does not exist in Route53" % (zone_in
                                                        or hosted_zone_id_in)
        module.fail_json(msg=errmsg)

    aws_record = get_record(route53, zone_id, record_in, type_in,
                            identifier_in)

    resource_record_set = scrub_none_parameters({
        'Name':
        record_in,
        'Type':
        type_in,
        'Weight':
        weight_in,
        'Region':
        region_in,
        'Failover':
        failover_in,
        'TTL':
        ttl_in,
        'ResourceRecords': [dict(Value=value) for value in value_in],
        'HealthCheckId':
        health_check_in,
    })

    if alias_in:
        resource_record_set['AliasTarget'] = dict(
            HostedZoneId=alias_hosted_zone_id_in,
            DNSName=value_in[0],
            EvaluateTargetHealth=alias_evaluate_target_health_in)
        if 'ResourceRecords' in resource_record_set:
            del resource_record_set['ResourceRecords']
        if 'TTL' in resource_record_set:
            del resource_record_set['TTL']

    # On CAA records order doesn't matter
    if type_in == 'CAA':
        resource_record_set['ResourceRecords'] = sorted(
            resource_record_set['ResourceRecords'], key=itemgetter('Value'))
        if aws_record:
            aws_record['ResourceRecords'] = sorted(
                aws_record['ResourceRecords'], key=itemgetter('Value'))

    if command_in == 'create' and aws_record == resource_record_set:
        rr_sets = [camel_dict_to_snake_dict(resource_record_set)]
        module.exit_json(changed=False, resource_records_sets=rr_sets)

    if command_in == 'get':
        if type_in == 'NS':
            ns = aws_record.get('values', [])
        else:
            # Retrieve name servers associated to the zone.
            ns = get_hosted_zone_nameservers(route53, zone_id)

        formatted_aws = format_record(aws_record, zone_in, zone_id)
        rr_sets = [camel_dict_to_snake_dict(aws_record)]
        module.exit_json(changed=False,
                         set=formatted_aws,
                         nameservers=ns,
                         resource_record_sets=rr_sets)

    if command_in == 'delete' and not aws_record:
        module.exit_json(changed=False)

    if command_in == 'create' or command_in == 'delete':
        if command_in == 'create' and aws_record:
            if not module.params['overwrite']:
                module.fail_json(
                    msg=
                    "Record already exists with different value. Set 'overwrite' to replace it"
                )
            command = 'UPSERT'
        else:
            command = command_in.upper()

    if not module.check_mode:
        try:
            change_resource_record_sets = route53.change_resource_record_sets(
                aws_retry=True,
                HostedZoneId=zone_id,
                ChangeBatch=dict(Changes=[
                    dict(Action=command, ResourceRecordSet=resource_record_set)
                ]))

            if wait_in:
                waiter = get_waiter(route53, 'resource_record_sets_changed')
                waiter.wait(Id=change_resource_record_sets['ChangeInfo']['Id'],
                            WaiterConfig=dict(
                                Delay=WAIT_RETRY,
                                MaxAttempts=wait_timeout_in // WAIT_RETRY,
                            ))
        except is_boto3_error_message('but it already exists'):
            module.exit_json(changed=False)
        except botocore.exceptions.WaiterError as e:
            module.fail_json_aws(
                e,
                msg='Timeout waiting for resource records changes to be applied'
            )
        except (botocore.exceptions.BotoCoreError,
                botocore.exceptions.ClientError) as e:  # pylint: disable=duplicate-except
            module.fail_json_aws(e, msg='Failed to update records')
        except Exception as e:
            module.fail_json(msg='Unhandled exception. (%s)' % to_native(e))

    rr_sets = [camel_dict_to_snake_dict(resource_record_set)]
    formatted_aws = format_record(aws_record, zone_in, zone_id)
    formatted_record = format_record(resource_record_set, zone_in, zone_id)

    module.exit_json(
        changed=True,
        diff=dict(
            before=formatted_aws,
            after=formatted_record if command != 'delete' else {},
            resource_record_sets=rr_sets,
        ),
    )
Exemplo n.º 7
0
    def get_secret_value(self,
                         term,
                         client,
                         version_stage=None,
                         version_id=None,
                         on_missing=None,
                         on_denied=None,
                         on_deleted=None,
                         nested=False):
        params = {}
        params['SecretId'] = term
        if version_id:
            params['VersionId'] = version_id
        if version_stage:
            params['VersionStage'] = version_stage
        if nested:
            if len(term.split('.')) < 2:
                raise AnsibleError(
                    "Nested query must use the following syntax: `aws_secret_name.<key_name>.<key_name>"
                )
            secret_name = term.split('.')[0]
            params['SecretId'] = secret_name

        try:
            response = client.get_secret_value(**params)
            if 'SecretBinary' in response:
                return response['SecretBinary']
            if 'SecretString' in response:
                if nested:
                    secrets = []
                    query = term.split('.')[1:]
                    secret_string = json.loads(response['SecretString'])
                    ret_val = secret_string
                    for key in query:
                        if key in ret_val:
                            ret_val = ret_val[key]
                        else:
                            raise AnsibleError(
                                "Successfully retrieved secret but there exists no key {0} in the secret"
                                .format(key))
                    return str(ret_val)
                else:
                    return response['SecretString']
        except is_boto3_error_message('marked for deletion'):
            if on_deleted == 'error':
                raise AnsibleError(
                    "Failed to find secret %s (marked for deletion)" % term)
            elif on_deleted == 'warn':
                self._display.warning(
                    'Skipping, did not find secret (marked for deletion) %s' %
                    term)
        except is_boto3_error_code('ResourceNotFoundException'):  # pylint: disable=duplicate-except
            if on_missing == 'error':
                raise AnsibleError(
                    "Failed to find secret %s (ResourceNotFound)" % term)
            elif on_missing == 'warn':
                self._display.warning('Skipping, did not find secret %s' %
                                      term)
        except is_boto3_error_code('AccessDeniedException'):  # pylint: disable=duplicate-except
            if on_denied == 'error':
                raise AnsibleError(
                    "Failed to access secret %s (AccessDenied)" % term)
            elif on_denied == 'warn':
                self._display.warning('Skipping, access denied for secret %s' %
                                      term)
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:  # pylint: disable=duplicate-except
            raise AnsibleError("Failed to retrieve secret: %s" % to_native(e))

        return None