diff --git a/ci/roles/keystone_idp/defaults/main.yml b/ci/roles/keystone_idp/defaults/main.yml new file mode 100644 index 00000000..0698ab97 --- /dev/null +++ b/ci/roles/keystone_idp/defaults/main.yml @@ -0,0 +1,13 @@ +idp_name: 'test-idp' +idp_name_2: 'test-idp-2' +idp_description: 'My example IDP' +idp_description_2: 'My example Identity Provider' + +domain_name: 'test-domain' +remote_ids_1: +- 'https://auth.example.com/auth/realms/ExampleRealm' +- 'https://auth.stage.example.com/auth/realms/ExampleRealm' +remote_ids_2: +- 'https://auth.example.com/auth/realms/ExampleRealm' +remote_ids_3: +- 'https://auth.stage.example.com/auth/realms/ExampleRealm' diff --git a/ci/roles/keystone_idp/tasks/main.yml b/ci/roles/keystone_idp/tasks/main.yml new file mode 100644 index 00000000..85ac3341 --- /dev/null +++ b/ci/roles/keystone_idp/tasks/main.yml @@ -0,0 +1,801 @@ +--- +# General run of tests +# - Make change - Check mode +# - Make change +# - Retry change (noop) - Check mode +# - Retry change (noop) +# +- module_defaults: + # meta/action_groups.yml glue seems to be missing + # group/os: + # cloud: "{{ cloud }}" + openstack.cloud.os_keystone_domain: + cloud: "{{ cloud }}" + openstack.cloud.os_keystone_identity_provider: + cloud: "{{ cloud }}" + #openstack.cloud.os_keystone_identity_provider_info: + # cloud: "{{ cloud }}" + block: + # ======================================================================== + # Initial setup + - name: 'Create test domain' + openstack.cloud.os_keystone_domain: + name: '{{ domain_name }}' + register: create_domain + - assert: + that: + - create_domain is successful + - '"id" in create_domain' + - name: 'Store domain ID as fact' + set_fact: + domain_id: '{{ create_domain.id }}' + + # We *should* have a blank slate to start with, but we also shouldn't + # explode if I(state=absent) and the IDP doesn't exist + - name: "Ensure IDP doesn't exist to start" + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + register: delete_idp + - assert: + that: + - delete_idp is successful + + # ======================================================================== + # Creation (simple case) + + - name: 'Create IDP - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + domain_id: '{{ domain_id }}' + register: create_idp + - assert: + that: + - create_idp is successful + - create_idp is changed + + #- name: 'Fetch identity_provider info (provider should be absent)' + # openstack.cloud.os_keystone_identity_provider_info: + # name: '{{ idp_name }}' + # register: identity_provider_info + # ignore_errors: yes + #- assert: + # that: + # - identity_provider_info is failed + + - name: 'Create IDP' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + domain_id: '{{ domain_id }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is changed + - '"identity_provider" in create_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - not idp.description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == [] + vars: + idp: '{{ create_identity_provider.identity_provider }}' + + #- name: 'Fetch IDP info - with name' + # openstack.cloud.os_keystone_identity_provider_info: + # name: '{{ idp_name }}' + # register: identity_provider_info + #- assert: + # that: + # - identity_provider_info is successful + # - '"identity_providers" in identity_provider_info' + # - idps | length == 1 + # - '"id" in idp' + # - '"name" in idp' + # - '"domain_id" in idp' + # - '"description" in idp' + # - '"enabled" in idp' + # - '"is_enabled" in idp' + # - '"remote_ids" in idp' + # - idp.id == idp_name + # - idp.name == idp_name + # - idp.domain_id == domain_id + # - not idp.description + # - idp.enabled == True + # - idp.is_enabled == True + # - idp.remote_ids == [] + # vars: + # idps: '{{ identity_provider_info.identity_providers }}' + # idp: '{{ identity_provider_info.identity_providers[0] }}' + + #- name: 'Fetch identity_provider info - without name' + # openstack.cloud.os_keystone_identity_provider_info: {} + # register: identity_provider_info + #- assert: + # that: + # - identity_provider_info is successful + # - '"identity_providers" in identity_provider_info' + # # In CI we generally have a clean slate, but this might + # # not be true for everyone... + # - idps | length >= 1 + # - '"id" in idp' + # - '"name" in idp' + # - '"domain_id" in idp' + # - '"description" in idp' + # - '"enabled" in idp' + # - '"is_enabled" in idp' + # - '"remote_ids" in idp' + # vars: + # idps: '{{ identity_provider_info.identity_providers }}' + # idp: '{{ identity_provider_info.identity_providers[0] }}' + + - name: 'Create identity_provider (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + domain_id: '{{ domain_id }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is not changed + + - name: 'Create identity_provider (retry - no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + domain_id: '{{ domain_id }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is not changed + - '"identity_provider" in create_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - not idp.description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == [] + vars: + idp: '{{ create_identity_provider.identity_provider }}' + + # ======================================================================== + # Update (simple cases) + + - name: 'Update IDP set description - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + + - name: 'Update IDP set description' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == [] + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + - name: 'Update IDP set description (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + + - name: 'Update IDP set description (retry - no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == [] + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + + - name: 'Update IDP set Remote IDs - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + remote_ids: '{{ remote_ids_1 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + + - name: 'Update IDP set Remote IDs' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + remote_ids: '{{ remote_ids_1 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == remote_ids_1 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + - name: 'Update IDP set Remote IDs (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + remote_ids: '{{ remote_ids_1 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + + - name: 'Update IDP set Remote IDs (retry - no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + remote_ids: '{{ remote_ids_1 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == remote_ids_1 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + + - name: 'Update IDP set Disabled - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + enabled: False + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + + - name: 'Update IDP set Disabled' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + enabled: False + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == False + - idp.is_enabled == False + - idp.remote_ids == remote_ids_1 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + - name: 'Update IDP set Disabled (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + enabled: False + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + + - name: 'Update IDP set Disabled (retry - no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + enabled: False + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == False + - idp.is_enabled == False + - idp.remote_ids == remote_ids_1 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + # If we don't specify anything to change, then nothing should change... + - name: 'Minimal call to IDP (no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + + - name: 'Minimal call to IDP (no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == False + - idp.is_enabled == False + - idp.remote_ids == remote_ids_1 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + # ======================================================================== + # Update (mass-update) + + - name: 'Update all updatable IDP parameters - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description_2 }}' + enabled: True + remote_ids: '{{ remote_ids_2 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + + - name: 'Update all updatable IDP parameters' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description_2 }}' + enabled: True + remote_ids: '{{ remote_ids_2 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description_2 + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == remote_ids_2 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + - name: 'Update all updatable IDP parameters (no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description_2 }}' + enabled: True + remote_ids: '{{ remote_ids_2 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + + - name: 'Update all updatable IDP parameters (no change)' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name }}' + description: '{{ idp_description_2 }}' + enabled: True + remote_ids: '{{ remote_ids_2 }}' + register: update_identity_provider + - assert: + that: + - update_identity_provider is successful + - update_identity_provider is not changed + - '"identity_provider" in update_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name + - idp.name == idp_name + - idp.domain_id == domain_id + - idp.description == idp_description_2 + - idp.enabled == True + - idp.is_enabled == True + - idp.remote_ids == remote_ids_2 + vars: + idp: '{{ update_identity_provider.identity_provider }}' + + # ======================================================================== + # Create complex IDP + + - name: 'Create complex IDP - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name_2 }}' + domain_id: '{{ domain_id }}' + description: '{{ idp_description }}' + enabled: False + remote_ids: '{{ remote_ids_3 }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is changed + + - name: 'Create complex IDP' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name_2 }}' + domain_id: '{{ domain_id }}' + description: '{{ idp_description }}' + enabled: False + remote_ids: '{{ remote_ids_3 }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is changed + - '"identity_provider" in create_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name_2 + - idp.name == idp_name_2 + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == False + - idp.is_enabled == False + - idp.remote_ids == remote_ids_3 + vars: + idp: '{{ create_identity_provider.identity_provider }}' + + - name: 'Create complex IDP (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name_2 }}' + domain_id: '{{ domain_id }}' + description: '{{ idp_description }}' + enabled: False + remote_ids: '{{ remote_ids_3 }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is not changed + + - name: 'Create complex IDP' + openstack.cloud.os_keystone_identity_provider: + state: 'present' + name: '{{ idp_name_2 }}' + domain_id: '{{ domain_id }}' + description: '{{ idp_description }}' + enabled: False + remote_ids: '{{ remote_ids_3 }}' + register: create_identity_provider + - assert: + that: + - create_identity_provider is successful + - create_identity_provider is not changed + - '"identity_provider" in create_identity_provider' + - '"id" in idp' + - '"name" in idp' + - '"domain_id" in idp' + - '"description" in idp' + - '"enabled" in idp' + - '"is_enabled" in idp' + - '"remote_ids" in idp' + - idp.id == idp_name_2 + - idp.name == idp_name_2 + - idp.domain_id == domain_id + - idp.description == idp_description + - idp.enabled == False + - idp.is_enabled == False + - idp.remote_ids == remote_ids_3 + vars: + idp: '{{ create_identity_provider.identity_provider }}' + + ## Attempt to ensure that if we search we only get the one we expect + #- name: 'Fetch Complex IDP info - with name' + # openstack.cloud.os_keystone_identity_provider_info: + # name: '{{ idp_name_2 }}' + # register: identity_provider_info + #- assert: + # that: + # - identity_provider_info is successful + # - '"identity_providers" in identity_provider_info' + # - identity_provider_info.identity_providers | length == 1 + # - '"id" in idp' + # - '"name" in idp' + # - '"domain_id" in idp' + # - '"description" in idp' + # - '"enabled" in idp' + # - '"is_enabled" in idp' + # - '"remote_ids" in idp' + # - idp.id == idp_name_2 + # - idp.name == idp_name_2 + # - idp.domain_id == domain_id + # - idp.description == idp_description + # - idp.enabled == False + # - idp.is_enabled == False + # - idp.remote_ids == remote_ids_3 + # vars: + # idp: '{{ identity_provider_info.identity_providers[0] }}' + + ## Ensure that if we do search we get both of the results we expect + #- name: 'Fetch multiple IDP info - without name' + # openstack.cloud.os_keystone_identity_provider_info: {} + # register: identity_provider_info + #- assert: + # that: + # - identity_provider_info is successful + # - '"identity_providers" in identity_provider_info' + # # In CI we generally have a clean slate, but this might + # # not be true for everyone... + # - identity_provider_info.identity_providers | length >= 2 + # - '"id" in idp_0' + # - '"name" in idp_0' + # - '"domain_id" in idp_0' + # - '"description" in idp_0' + # - '"enabled" in idp_0' + # - '"is_enabled" in idp_0' + # - '"remote_ids" in idp_0' + # - '"id" in idp_1' + # - '"name" in idp_1' + # - '"domain_id" in idp_1' + # - '"description" in idp_1' + # - '"enabled" in idp_1' + # - '"is_enabled" in idp_1' + # - '"remote_ids" in idp_1' + # # In theory these could be attached to different IDPs but let's keep + # # things simple + # - idp_name in (identity_provider_info.identity_providers | map(attribute='id')) + # - idp_name in (identity_provider_info.identity_providers | map(attribute='name')) + # - idp_name_2 in (identity_provider_info.identity_providers | map(attribute='id')) + # - idp_name_2 in (identity_provider_info.identity_providers | map(attribute='name')) + # - domain_id in (identity_provider_info.identity_providers | map(attribute='domain_id')) + # - idp_description in (identity_provider_info.identity_providers | map(attribute='description')) + # - idp_description_2 in (identity_provider_info.identity_providers | map(attribute='description')) + # - True in (identity_provider_info.identity_providers | map(attribute='enabled')) + # - False in (identity_provider_info.identity_providers | map(attribute='enabled')) + # - True in (identity_provider_info.identity_providers | map(attribute='is_enabled')) + # - False in (identity_provider_info.identity_providers | map(attribute='is_enabled')) + # vars: + # idp_0: '{{ identity_provider_info.identity_providers[0] }}' + # idp_1: '{{ identity_provider_info.identity_providers[1] }}' + + - name: 'Delete identity_provider - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + register: delete_identity_provider + - assert: + that: + - delete_identity_provider is successful + - delete_identity_provider is changed + + - name: 'Delete identity_provider' + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + register: delete_identity_provider + - assert: + that: + - delete_identity_provider is successful + - delete_identity_provider is changed + + - name: 'Delete identity_provider (retry - no change) - CHECK_MODE' + check_mode: yes + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + register: delete_identity_provider + - assert: + that: + - delete_identity_provider is successful + - delete_identity_provider is not changed + + - name: 'Delete identity_provider (retry - no change) ' + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + register: delete_identity_provider + - assert: + that: + - delete_identity_provider is successful + - delete_identity_provider is not changed + + #- name: 'Fetch identity_provider info after deletion' + # openstack.cloud.os_keystone_identity_provider_info: + # name: '{{ idp_name }}' + # register: identity_provider_info + # ignore_errors: True + #- assert: + # that: + # - identity_provider_info is failed + + - name: 'Delete second identity_provider' + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name_2 }}' + register: delete_identity_provider + - assert: + that: + - delete_identity_provider is successful + - delete_identity_provider is changed + + always: + - name: 'Delete idp' + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name }}' + ignore_errors: yes + + - name: 'Delete second identity_provider' + openstack.cloud.os_keystone_identity_provider: + state: 'absent' + name: '{{ idp_name_2 }}' + ignore_errors: yes + + - name: 'Delete domain' + openstack.cloud.os_keystone_domain: + state: 'absent' + name: '{{ domain_name }}' + ignore_errors: yes diff --git a/ci/run-collection.yml b/ci/run-collection.yml index cce5d41f..fabca7be 100644 --- a/ci/run-collection.yml +++ b/ci/run-collection.yml @@ -15,6 +15,9 @@ - role: keystone_mapping tags: keystone_mapping when: sdk_version is version(0.44, '>=') + - role: keystone_idp + tags: keystone_idp + when: sdk_version is version(0.44, '>=') - { role: keystone_role, tags: keystone_role } - { role: network, tags: network } - { role: nova_flavor, tags: nova_flavor } diff --git a/meta/action_groups.yml b/meta/action_groups.yml index be296e86..384f64b6 100644 --- a/meta/action_groups.yml +++ b/meta/action_groups.yml @@ -16,6 +16,7 @@ os: - os_keystone_domain - os_keystone_domain_info - os_keystone_endpoint +- os_keystone_identity_provider - os_keystone_mapping - os_keystone_mapping_info - os_keystone_role diff --git a/plugins/modules/os_keystone_identity_provider.py b/plugins/modules/os_keystone_identity_provider.py new file mode 100644 index 00000000..92f3cbd6 --- /dev/null +++ b/plugins/modules/os_keystone_identity_provider.py @@ -0,0 +1,248 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: os_keystone_identity_provider +short_description: manage a federation Identity Provider +author: + - "Mark Chappell (@tremble) " +description: + - Manage a federation Identity Provider. +options: + name: + description: + - The name of the Identity Provider. + type: str + required: true + aliases: ['id'] + state: + description: + - Whether the Identity Provider should be C(present) or C(absent). + choices: ['present', 'absent'] + default: present + type: str + description: + description: + - The description of the Identity Provider. + type: str + domain_id: + description: + - The ID of a domain that is associated with the Identity Provider. + Federated users that authenticate with the Identity Provider will be + created under the domain specified. + - Required when creating a new Identity Provider. + type: str + enabled: + description: + - Whether the Identity Provider is enabled or not. + - Will default to C(true) when creating a new Identity Provider. + type: bool + aliases: ['is_enabled'] + remote_ids: + description: + - "List of the unique Identity Provider's remote IDs." + - Will default to an empty list when creating a new Identity Provider. + type: list + elements: str +requirements: + - "python >= 3.6" + - "openstacksdk >= 0.44" +extends_documentation_fragment: + - openstack.cloud.openstack +''' + +EXAMPLES = ''' +- name: Create an identity provider + os_keystone_identity_provider: + cloud: example_cloud + name: example_provider + domain_id: 0123456789abcdef0123456789abcdef + description: 'My example IDP' + remote_ids: + - 'https://auth.example.com/auth/realms/ExampleRealm' + +- name: Delete an identity provider + os_keystone_identity_provider: + cloud: example_cloud + name: example_provider + state: absent +''' + +RETURN = ''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module + + +def normalize_idp(idp): + """ + Normalizes the IDP definitions so that the outputs are consistent with the + parameters + + - "enabled" (parameter) == "is_enabled" (SDK) + - "name" (parameter) == "id" (SDK) + """ + if idp is None: + return None + + _idp = idp.to_dict() + _idp['enabled'] = idp['is_enabled'] + _idp['name'] = idp['id'] + return _idp + + +def delete_identity_provider(module, sdk, cloud, idp): + """ + Delete an existing Identity Provider + + returns: the "Changed" state + """ + + if idp is None: + return False + + if module.check_mode: + return True + + try: + cloud.identity.delete_identity_provider(idp) + except sdk.exceptions.OpenStackCloudException as ex: + module.fail_json(msg='Failed to delete identity provider: {0}'.format(str(ex))) + return True + + +def create_identity_provider(module, sdk, cloud, name): + """ + Create a new Identity Provider + + returns: the "Changed" state and the new identity provider + """ + + if module.check_mode: + return True, None + + description = module.params.get('description') + enabled = module.params.get('enabled') + domain_id = module.params.get('domain_id') + remote_ids = module.params.get('remote_ids') + + if enabled is None: + enabled = True + if remote_ids is None: + remote_ids = [] + + attributes = { + 'domain_id': domain_id, + 'enabled': enabled, + 'remote_ids': remote_ids, + } + if description is not None: + attributes['description'] = description + + try: + idp = cloud.identity.create_identity_provider(id=name, **attributes) + except sdk.exceptions.OpenStackCloudException as ex: + module.fail_json(msg='Failed to create identity provider: {0}'.format(str(ex))) + return (True, idp) + + +def update_identity_provider(module, sdk, cloud, idp): + """ + Update an existing Identity Provider + + returns: the "Changed" state and the new identity provider + """ + + description = module.params.get('description') + enabled = module.params.get('enabled') + domain_id = module.params.get('domain_id') + remote_ids = module.params.get('remote_ids') + + attributes = {} + + if (description is not None) and (description != idp.description): + attributes['description'] = description + if (enabled is not None) and (enabled != idp.is_enabled): + attributes['enabled'] = enabled + if (domain_id is not None) and (domain_id != idp.domain_id): + attributes['domain_id'] = domain_id + if (remote_ids is not None) and (remote_ids != idp.remote_ids): + attributes['remote_ids'] = remote_ids + + if not attributes: + return False, idp + + if module.check_mode: + return True, None + + try: + new_idp = cloud.identity.update_identity_provider(idp, **attributes) + except sdk.exceptions.OpenStackCloudException as ex: + module.fail_json(msg='Failed to update identity provider: {0}'.format(str(ex))) + return (True, new_idp) + + +def main(): + """ Module entry point """ + + argument_spec = openstack_full_argument_spec( + name=dict(required=True, aliases=['id']), + state=dict(default='present', choices=['absent', 'present']), + description=dict(), + domain_id=dict(), + enabled=dict(type='bool', aliases=['is_enabled']), + remote_ids=dict(type='list', elements='str'), + ) + module_kwargs = openstack_module_kwargs( + ) + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + **module_kwargs + ) + + name = module.params.get('name') + state = module.params.get('state') + changed = False + + sdk, cloud = openstack_cloud_from_module(module, min_version="0.44") + + try: + idp = cloud.identity.get_identity_provider(name) + except sdk.exceptions.ResourceNotFound: + idp = None + except sdk.exceptions.OpenStackCloudException as ex: + module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex))) + + if state == 'absent': + if idp is not None: + changed = delete_identity_provider(module, sdk, cloud, idp) + module.exit_json(changed=changed) + + # state == 'present' + else: + if idp is None: + if module.params.get('domain_id') is None: + module.fail_json(msg='A domain_id must be passed when creating' + ' an identity provider') + (changed, idp) = create_identity_provider(module, sdk, cloud, name) + idp = normalize_idp(idp) + module.exit_json(changed=changed, identity_provider=idp) + + (changed, new_idp) = update_identity_provider(module, sdk, cloud, idp) + new_idp = normalize_idp(new_idp) + module.exit_json(changed=changed, identity_provider=new_idp) + + +if __name__ == '__main__': + main()