---

- module_defaults:
    group/aws:
      aws_access_key: '{{ aws_access_key | default(omit) }}'
      aws_secret_key: '{{ aws_secret_key | default(omit) }}'
      security_token: '{{ security_token | default(omit) }}'
      region: '{{ aws_region | default(omit) }}'

  collections:
    - amazon.aws

  block:
    - name: list available AZs
      aws_az_info:
      register: region_azs

    - name: pick an AZ for testing
      set_fact:
        availability_zone: "{{ region_azs.availability_zones[0].zone_name }}"

    - name: Create a test VPC
      ec2_vpc_net:
        name: "{{ vpc_name }}"
        cidr_block: "{{ vpc_cidr }}"
        tags:
          Name: ec2_vol testing
          ResourcePrefix: "{{ resource_prefix }}"
      register: testing_vpc

    - name: Create a test subnet
      ec2_vpc_subnet:
        vpc_id: "{{ testing_vpc.vpc.id }}"
        cidr: "{{ subnet_cidr }}"
        tags:
          Name: ec2_vol testing
          ResourcePrefix: "{{ resource_prefix }}"
        az: '{{ availability_zone }}'
      register: testing_subnet

    - name: Find AMI to use
      ec2_ami_info:
        owners: 'amazon'
        filters:
          name: '{{ ec2_ami_name }}'
      register: ec2_amis

    - name: Set fact with latest AMI
      vars:
        latest_ami: '{{ ec2_amis.images | sort(attribute="creation_date") | last }}'
      set_fact:
        ec2_ami_image: '{{ latest_ami.image_id }}'

    # ==== ec2_vol tests ===============================================

    - name: create a volume (validate module defaults)
      ec2_vol:
        volume_size: 1
        zone: "{{ availability_zone }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: volume1

    - name: check task return attributes
      assert:
        that:
          - volume1.changed
          - "'volume' in volume1"
          - "'volume_id' in volume1"
          - "'volume_type' in volume1"
          - "'device' in volume1"
          - volume1.volume.status == 'available'
          - volume1.volume_type == 'standard'
          - "'attachment_set' in volume1.volume"
          - "'instance_id' in volume1.volume.attachment_set"
          - not volume1.volume.attachment_set.instance_id
          - not volume1.volume.encrypted
          - volume1.volume.tags.ResourcePrefix == "{{ resource_prefix }}"

    # no idempotency check needed here

    - name: create another volume (override module defaults)
      ec2_vol:
        encrypted: yes
        volume_size: 4
        volume_type: io1
        iops: 101
        name: "{{ resource_prefix }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
        zone: "{{ availability_zone }}"
      register: volume2

    - name: check task return attributes
      assert:
        that:
          - volume2.changed
          - "'volume' in volume2"
          - "'volume_id' in volume2"
          - "'volume_type' in volume2"
          - "'device' in volume2"
          - volume2.volume.status == 'available'
          - volume2.volume_type == 'io1'
          - volume2.volume.iops == 101
          - volume2.volume.size == 4
          - volume2.volume.encrypted
          - volume2.volume.tags.ResourcePrefix == "{{ resource_prefix }}"

    - name: create another volume (override module defaults) (idempotent)
      ec2_vol:
        encrypted: yes
        volume_size: 4
        volume_type: io1
        iops: 101
        name: "{{ resource_prefix }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
        zone: "{{ availability_zone }}"
      register: volume2_idem

    - name: check task return attributes
      assert:
        that:
          - not volume2_idem.changed

    - name: create snapshot from volume
      ec2_snapshot:
        volume_id: "{{ volume1.volume_id }}"
        description: "Resource Prefix - {{ resource_prefix }}"
        snapshot_tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: vol1_snapshot

    - name: check task return attributes
      assert:
        that:
          - vol1_snapshot.changed

    - name: create a volume from a snapshot
      ec2_vol:
        snapshot: "{{ vol1_snapshot.snapshot_id }}"
        encrypted: yes
        volume_type: gp2
        volume_size: 1
        zone: "{{ availability_zone }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: volume3

    - name: check task return attributes
      assert:
        that:
          - volume3.changed
          - "volume3.volume.snapshot_id ==  vol1_snapshot.snapshot_id"

    - name: create an ec2 instance
      ec2_instance:
        name: "{{ resource_prefix }}"
        vpc_subnet_id: "{{ testing_subnet.subnet.id }}"
        instance_type: t3.nano
        image_id: "{{ ec2_ami_image }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: test_instance

    - name: check task return attributes
      assert:
        that:
          - test_instance.changed

    - name: attach existing volume to an instance
      ec2_vol:
        id: "{{ volume1.volume_id }}"
        instance: "{{ test_instance.instance_ids[0] }}"
        device_name: /dev/sdg
        delete_on_termination: no
      register: vol_attach_result

    - name: check task return attributes
      assert:
        that:
          - vol_attach_result.changed
          - "'device' in vol_attach_result and vol_attach_result.device == '/dev/sdg'"
          - "'volume' in vol_attach_result"
          - vol_attach_result.volume.attachment_set.status in ['attached', 'attaching']
          - vol_attach_result.volume.attachment_set.instance_id == test_instance.instance_ids[0]
          - vol_attach_result.volume.attachment_set.device == '/dev/sdg'

#          Failing
#          - "vol_attach_result.volume.attachment_set.deleteOnTermination"

    - name: attach existing volume to an instance (idempotent)
      ec2_vol:
        id: "{{ volume1.volume_id }}"
        instance: "{{ test_instance.instance_ids[0] }}"
        device_name: /dev/sdg
        delete_on_termination: no
      register: vol_attach_result

    - name: check task return attributes
      assert:
        that:
          - "not vol_attach_result.changed"
          - "vol_attach_result.volume.attachment_set.status == 'attached'"

    - name: attach a new volume to an instance
      ec2_vol:
        instance: "{{ test_instance.instance_ids[0] }}"
        device_name: /dev/sdh
        volume_size: 1
        volume_type: gp2
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: new_vol_attach_result

    - name: check task return attributes
      assert:
        that:
          - new_vol_attach_result.changed
          - "'device' in new_vol_attach_result and new_vol_attach_result.device == '/dev/sdh'"
          - "'volume' in new_vol_attach_result"
          - new_vol_attach_result.volume.attachment_set.status in ['attached', 'attaching']
          - new_vol_attach_result.volume.attachment_set.instance_id == test_instance.instance_ids[0]
          - new_vol_attach_result.volume.attachment_set.device == '/dev/sdh'

    - name: attach a new volume to an instance (idempotent)
      ec2_vol:
        instance: "{{ test_instance.instance_ids[0] }}"
        device_name: /dev/sdh
        volume_size: 1
        volume_type: gp2
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: new_vol_attach_result_idem
      ignore_errors: true

    - name: check task return attributes
      assert:
        that:
          - "not new_vol_attach_result_idem.changed"
          - "'Volume mapping for /dev/sdh already exists' in new_vol_attach_result_idem.msg"

    - name: create a volume from a snapshot and attach to the instance
      ec2_vol:
        instance: "{{ test_instance.instance_ids[0] }}"
        device_name: /dev/sdi
        snapshot: "{{ vol1_snapshot.snapshot_id }}"
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: attach_new_vol_from_snapshot_result

    - name: check task return attributes
      assert:
        that:
          - attach_new_vol_from_snapshot_result.changed
          - "'device' in attach_new_vol_from_snapshot_result and attach_new_vol_from_snapshot_result.device == '/dev/sdi'"
          - "'volume' in attach_new_vol_from_snapshot_result"
          - attach_new_vol_from_snapshot_result.volume.attachment_set.status in ['attached', 'attaching']
          - attach_new_vol_from_snapshot_result.volume.attachment_set.instance_id == test_instance.instance_ids[0]

    - name: list volumes attached to instance
      ec2_vol:
        instance: "{{ test_instance.instance_ids[0] }}"
        state: list
      register: inst_vols

    - name: check task return attributes
      assert:
        that:
          - not inst_vols.changed
          - "'volumes' in inst_vols"
          - inst_vols.volumes | length == 4

    - name: get info on ebs volumes
      ec2_vol_info:
      register: ec2_vol_info

    - name: check task return attributes
      assert:
        that:
          - not ec2_vol_info.failed

    - name: get info on ebs volumes
      ec2_vol_info:
        filters:
          attachment.instance-id: "{{ test_instance.instance_ids[0] }}"
      register: ec2_vol_info

    - name: check task return attributes
      assert:
        that:
          - ec2_vol_info.volumes | length == 4

    - name: detach volume from the instance
      ec2_vol:
        id: "{{ new_vol_attach_result.volume_id }}"
        instance: ""
      register: new_vol_attach_result

    - name: check task return attributes
      assert:
        that:
          - new_vol_attach_result.changed
          - new_vol_attach_result.volume.status == 'available'

    - name: detach volume from the instance (idempotent)
      ec2_vol:
        id: "{{ new_vol_attach_result.volume_id }}"
        instance: ""
      register: new_vol_attach_result_idem

    - name: check task return attributes
      assert:
        that:
          - not new_vol_attach_result_idem.changed

    - name: must not change because of missing parameter modify_volume
      ec2_vol:
        id: "{{ new_vol_attach_result.volume_id }}"
        zone: "{{ availability_zone }}"
        volume_type: gp3
      register: changed_gp3_volume

    - name: volume must not changed
      assert:
        that:
          - not changed_gp3_volume.changed

    - name: change existing volume to gp3
      ec2_vol:
        id: "{{ new_vol_attach_result.volume_id }}"
        zone: "{{ availability_zone }}"
        volume_type: gp3
        modify_volume: yes
      register: changed_gp3_volume

    - name: check that volume_type has changed
      assert:
        that:
          - changed_gp3_volume.volume_type == 'gp3'
          - changed_gp3_volume.changed

    - name: volume must be from type gp3 (idempotent)
      ec2_vol:
        id: "{{ new_vol_attach_result.volume_id }}"
        zone: "{{ availability_zone }}"
        volume_type: gp3
        modify_volume: yes
      register: changed_gp3_volume
      retries: 3
      delay: 3
      until: not changed_gp3_volume.failed
      # retry because ebs change is to slow

    - name: must not changed (idempotent)
      assert:
        that:
          - changed_gp3_volume.volume_type == 'gp3'
          - not changed_gp3_volume.changed

    - name: re-read volume information to validate new volume_type
      ec2_vol_info:
        filters:
          volume-id: "{{ changed_gp3_volume.volume_id }}"
      register: verify_gp3_change

    - name: volume type must be gp3
      assert:
        that:
          - v.type == 'gp3'
      vars:
        v: "{{ verify_gp3_change.volumes[0] }}"

    - name: delete volume
      ec2_vol:
        id: "{{ volume2.volume_id }}"
        state: absent
      register: delete_volume_result

    - name: check task return attributes
      assert:
        that:
          - "delete_volume_result.changed"

    - name: delete volume (idempotent)
      ec2_vol:
        id: "{{ volume2.volume_id }}"
        state: absent
      register: delete_volume_result_idem

    - name: check task return attributes
      assert:
        that:
          - not delete_volume_result_idem.changed
          - '"Volume {{ volume2.volume_id }} does not exist" in delete_volume_result_idem.msg'

    # Originally from ec2_vol_info

    - name: Create test volume with Destroy on Terminate
      ec2_vol:
        instance: "{{ test_instance.instance_ids[0] }}"
        volume_size: 4
        name: "{{ resource_prefix }}_delete_on_terminate"
        device_name: /dev/sdj
        iops: 100
        tags:
            Tag Name with Space-and-dash: Tag Value with Space-and-dash
        delete_on_termination: yes
      register: dot_volume

    - name: Gather volume info without any filters
      ec2_vol_info:
      register: volume_info_wo_filters
      check_mode: no

    - name: Check if info are returned without filters
      assert:
        that:
          - "volume_info_wo_filters.volumes is defined"

    - name: Gather volume info
      ec2_vol_info:
          filters:
            "tag:Name": "{{ resource_prefix }}_delete_on_terminate"
      register: volume_info
      check_mode: no

    - name: Format check
      assert:
        that:
            - "volume_info.volumes|length == 1"
            - "v.attachment_set.attach_time is defined"
            - "v.attachment_set.device      is defined and v.attachment_set.device      == dot_volume.device"
            - "v.attachment_set.instance_id is defined and v.attachment_set.instance_id == test_instance.instance_ids[0]"
            - "v.attachment_set.status      is defined and v.attachment_set.status      == 'attached'"
            - "v.create_time                is defined"
            - "v.encrypted                  is defined and v.encrypted                  == false"
            - "v.id                         is defined and v.id                         == dot_volume.volume_id"
            - "v.iops                       is defined and v.iops                       == 100"
            - "v.region                     is defined and v.region                     == aws_region"
            - "v.size                       is defined and v.size                       == 4"
            - "v.snapshot_id                is defined and v.snapshot_id                == ''"
            - "v.status                     is defined and v.status                     == 'in-use'"
            - "v.tags.Name                  is defined and v.tags.Name                  == resource_prefix + '_delete_on_terminate'"
            - "v.tags['Tag Name with Space-and-dash']                                   == 'Tag Value with Space-and-dash'"
            - "v.type                       is defined and v.type                       == 'io1'"
            - "v.zone                       is defined and v.zone                       == test_instance.instances[0].placement.availability_zone"
      vars:
          v: "{{ volume_info.volumes[0] }}"

    - name: New format check
      assert:
        that:
            - "v.attachment_set.delete_on_termination is defined"
      vars:
          v: "{{ volume_info.volumes[0] }}"
      when: ansible_version.full is version('2.7', '>=')

    - name: test create a new gp3 volume
      ec2_vol:
        volume_size: 1
        volume_type: gp3
        zone: "{{ availability_zone }}"
        throughput: 130
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: gp3_volume

    - name: check that volume_type is gp3
      assert:
        that:
          - gp3_volume.volume_type == 'gp3'
          - gp3_volume.changed
          - gp3_volume.volume.throughput == 130

    - name: increase throughput
      ec2_vol:
        volume_size: 1
        volume_type: gp3
        zone: "{{ availability_zone }}"
        throughput: 131
        tags:
          ResourcePrefix: "{{ resource_prefix }}"
      register: gp3_volume

    - name: check that throughput has changed
      assert:
        that:
          - gp3_volume.volume_type == 'gp3'
          - gp3_volume.changed
          - gp3_volume.volume.throughput == 131


    # ==== Cleanup ============================================================

  always:
    - name: Describe the instance before we delete it
      ec2_instance_info:
        instance_ids:
          - "{{ test_instance.instance_ids[0] }}"
      ignore_errors: yes
      register: pre_delete

    - debug:
        var: pre_delete

    - name: delete test instance
      ec2_instance:
        instance_ids:
          - "{{ test_instance.instance_ids[0] }}"
        state: terminated
      ignore_errors: yes

    - name: delete volumes
      ec2_vol:
        id: "{{ item.volume_id }}"
        state: absent
      ignore_errors: yes
      with_items:
        - "{{ volume1 }}"
        - "{{ volume2 }}"
        - "{{ volume3 }}"
        - "{{ new_vol_attach_result }}"
        - "{{ attach_new_vol_from_snapshot_result }}"
        - "{{ dot_volume }}"
        - "{{ gp3_volume }}"

    - name: delete snapshot
      ec2_snapshot:
        snapshot_id: "{{ vol1_snapshot.snapshot_id }}"
        state: absent
      ignore_errors: yes

    - name: delete test subnet
      ec2_vpc_subnet:
        vpc_id: "{{ testing_vpc.vpc.id }}"
        cidr: "{{ subnet_cidr }}"
        state: absent
      ignore_errors: yes

    - name: delete test VPC
      ec2_vpc_net:
        name: "{{ vpc_name }}"
        cidr_block: "{{ vpc_cidr }}"
        state: absent
      ignore_errors: yes
