diff --git a/.github/workflows/integ-test.yml b/.github/workflows/integ-test.yml index f56dac717..be1de0fca 100644 --- a/.github/workflows/integ-test.yml +++ b/.github/workflows/integ-test.yml @@ -16,16 +16,17 @@ on: # git_issues - slow, do not run on each push. TODO - run them only once a day # oidc_config - during reconfiguration API returns 500/502 errors for other requests # smtp - email_alert test requires a configured SMTP + # role_ - role cluster_config reconfigures DNS, SMTP, OIDC. integ_tests_exclude: type: string description: |- List integration tests to exclude. Use "*" to exclude all tests. Use regex like 'node|^git_issue|^dns_config$' to exclude only a subset. - default: "^dns_config$|^cluster_shutdown$|^oidc_config$|^smtp$" + default: "^dns_config$|^cluster_shutdown$|^oidc_config$|^smtp$|^role_" env: INTEG_TESTS_INCLUDE_SCHEDULE: "*" - INTEG_TESTS_EXCLUDE_SCHEDULE: "^dns_config$|^cluster_shutdown$|^oidc_config$|^smtp$" + INTEG_TESTS_EXCLUDE_SCHEDULE: "^dns_config$|^cluster_shutdown$|^oidc_config$|^smtp$|^role_" WORKDIR: /work-dir/ansible_collections/scale_computing/hypercore # Run only one workflow for specific branch. concurrency: diff --git a/changelogs/fragments/cluster_config_role.yml b/changelogs/fragments/cluster_config_role.yml new file mode 100644 index 000000000..dadcb612c --- /dev/null +++ b/changelogs/fragments/cluster_config_role.yml @@ -0,0 +1,3 @@ +--- +major_changes: + - Added a role for cluster configuration (registration data, DNS resolver, SMPT server, email alert recipients, etc). diff --git a/examples/cluster_config.yml b/examples/cluster_config.yml new file mode 100644 index 000000000..28e0df145 --- /dev/null +++ b/examples/cluster_config.yml @@ -0,0 +1,53 @@ +--- +- name: Reconfigure HyperCore cluster settings + hosts: localhost + connection: local + gather_facts: false + # We do not want to reconfigure cluster with bogus example values. + check_mode: true + + vars: + cluster_configuration: + name: cluster-a + registration: + company_name: New company + contact: John Smit + phone: 056789000 + email: john_smith@gmail.com + dns: + server_ips: + - 1.1.1.1 + - 1.0.0.1 + search_domains: [] + # After OIDC is configured, it cannot be removed. + # Do not configure OIDC in example. + # oidc: + # client_id: your_client_id + # shared_secret: your_shared_secret + # # certificate: plain_text_from_x509 + # config_url: https://login.microsoftonline.com/your_tenant_uuid/v2.0/.well-known/openid-configuration + # scopes: openid+profile + time_server: pool.ntp.org + time_zone: Europe/Ljubljana + smtp: + server: smtp-relay.gmail.com + port: 25 + use_ssl: false + from_address: cluster-a@example.com + email_alerts: + - admin@example.com + syslog_servers: + - host: 10.10.10.10 + port: 514 + protocol: udp + + tasks: + - name: Show configuration + ansible.builtin.debug: + var: cluster_configuration + + - name: Configure HyperCore cluster + include_role: + name: scale_computing.hypercore.cluster_config + vars: + scale_computing_hypercore_cluster_config: "{{ cluster_configuration }}" diff --git a/roles/cluster_config/README.md b/roles/cluster_config/README.md new file mode 100644 index 000000000..e606e7787 --- /dev/null +++ b/roles/cluster_config/README.md @@ -0,0 +1,31 @@ +# cluster_config + +Role cluster_config can be used to: +- fully configure a new HyperCore server +- partially reconfigure an existing HyperCore server + +## Requirements + +- NA + +## Role Variables + +See [argument_specs.yml](../../roles/cluster_config/meta/argument_specs.yml). + +## Limitations + +- NA + +## Dependencies + +- NA + +## Example Playbook + +See [cluster_config.yml](../../examples/cluster_config.yml). + +## License + +GNU General Public License v3.0 or later + +See [LICENSE](../../LICENSE) to see the full text. diff --git a/roles/cluster_config/meta/argument_specs.yml b/roles/cluster_config/meta/argument_specs.yml new file mode 100644 index 000000000..e71b9b5c6 --- /dev/null +++ b/roles/cluster_config/meta/argument_specs.yml @@ -0,0 +1,172 @@ +argument_specs: + + main: + short_description: Configure HyperCore cluster + description: + - Role cluster_config can be used to + fully configure a new HyperCore server, + or partially reconfigure an existing HyperCore server. + options: + scale_computing_hypercore_cluster_config: + description: + - A dict describing a full or partial cluster configuration. + - Partial configuration will be used if some of the keys + in `scale_computing_hypercore_cluster_config` are omitted. + required: true + type: dict + options: + name: + description: + - Cluster name. + - If missing, cluster name will not be changed. + - See also M(scale_computing.hypercore.cluster_name). + required: false + type: str + registration: + description: + - Cluster registration data. + - If missing, cluster registration data will not be changed. + - See also M(scale_computing.hypercore.registration). + required: false + type: dict + options: + company_name: + description: Company name + required: true + type: str + contact: + description: Technical contact first and second name + required: true + type: str + phone: + description: Technical contact phone number + required: true + type: str + email: + description: Technical contact email address + required: true + type: str + dns: + description: + - DNS configuration. + - If missing, cluster DNS configuration will not be changed. + - See also M(scale_computing.hypercore.dns_config). + required: false + type: dict + options: + server_ips: + description: DNS resolver IP. + required: false + type: list + elements: str + search_domains: + description: DNS search domain. + required: false + type: list + elements: str + oidc: + description: + - OpenID connect configuration allows using Microsoft Azure as authentication backend. + - If missing, cluster OpenID connect configuration will not be changed. + - See also M(scale_computing.hypercore.oidc_config). + required: false + type: dict + options: + client_id: + description: OIDC client ID. + required: true + type: str + shared_secret: + description: OIDC client secret. + required: false + type: str + certificate: + description: OIDC client certificate, PEM encoded. + required: false + type: str + config_url: + description: OIDC configuration URL (for example, https://auth.example.com/.well-known/openid-configuration). + required: true + type: str + scopes: + description: OIDC client scopes. + required: true + type: str + time_server: + description: + - Cluster NTP time server. + - If missing, cluster NTP time server will not be changed. + - See also M(scale_computing.hypercore.time_server). + required: false + type: str + time_zone: + description: + - Cluster time zone. + - If missing, cluster time zone will not be changed. + - See also M(scale_computing.hypercore.time_zone). + required: false + type: str # choices: + smtp: + description: + - Cluster SMTP server configuration. + - If missing, cluster SMTP server will not be changed. + - See also M(scale_computing.hypercore.smtp). + required: false + type: dict + options: + server: + description: SMTP server (IP or DNS name). + required: true + type: str + port: + description: SMTP server TCP port. + required: true + type: int + use_ssl: + description: Use SSL/TLS encryption between HyperCore and SMTP server. + required: false + type: bool + auth_user: + description: Username to authenticate against SMTP server. + required: false + type: str + auth_password: + description: Password to authenticate against SMTP server. + required: false + type: str + from_address: + description: The "From" email address for email alerts. + required: true + type: str + email_alerts: + description: + - Email addresses that will receive email alerts. + - If missing, Email alert recipients will not be changed. + - See also M(scale_computing.hypercore.email_alert). + required: false + type: list + elements: str + syslog_servers: + description: + - Cluster syslog server configuration. + - If missing, cluster syslog server will not be changed. + - See also M(scale_computing.hypercore.syslog_server). + required: false + type: list + elements: dict + options: + host: + description: Syslog server IP address or DNS name. + required: true + type: str + port: + description: The IP port syslog server is listening to. + required: false + type: int + protocol: + description: Syslog IP protocol. + required: false + type: str + choices: + - udp + - tcp diff --git a/roles/cluster_config/tasks/main.yml b/roles/cluster_config/tasks/main.yml new file mode 100644 index 000000000..ce8e16479 --- /dev/null +++ b/roles/cluster_config/tasks/main.yml @@ -0,0 +1,126 @@ +--- +# Role configures HyperCore to state specified in scale_computing_hypercore_cluster_config. +# Partial (re)configuration is possible - missing configuration values are not reconfigured. +# If you need to remove some configuration, you can: +# - provide explicit empty value ("" or [] or {}) for given configuration entry +# - or call corresponding plugin with state=absent + +- name: Set cluster name + scale_computing.hypercore.cluster_name: + name_new: "{{ scale_computing_hypercore_cluster_config.name }}" + when: scale_computing_hypercore_cluster_config.name | default(False) + +- name: Set registration data + scale_computing.hypercore.registration: + company_name: "{{ scale_computing_hypercore_cluster_config.registration.company_name }}" + contact: "{{ scale_computing_hypercore_cluster_config.registration.contact }}" + phone: "{{ scale_computing_hypercore_cluster_config.registration.phone }}" + email: "{{ scale_computing_hypercore_cluster_config.registration.email }}" + state: present + when: scale_computing_hypercore_cluster_config.registration | default(False) + +- name: Set DNS servers configuration + scale_computing.hypercore.dns_config: + dns_servers: "{{ scale_computing_hypercore_cluster_config.dns.server_ips | default(omit) }}" + state: set + when: + - scale_computing_hypercore_cluster_config.dns | default(False) + - '"server_ips" in scale_computing_hypercore_cluster_config.dns' + - scale_computing_hypercore_cluster_config.dns.server_ips is not none + +- name: Set DNS search_domains configuration + scale_computing.hypercore.dns_config: + dns_servers: "{{ scale_computing_hypercore_cluster_config.dns.server_ips | default(omit) }}" + search_domains: "{{ scale_computing_hypercore_cluster_config.dns.search_domains | default(omit) }}" + state: set + when: + - scale_computing_hypercore_cluster_config.dns | default(False) + - '"search_domains" in scale_computing_hypercore_cluster_config.dns' + - scale_computing_hypercore_cluster_config.dns.search_domains is not none + +- name: Set OIDC configuration + scale_computing.hypercore.oidc_config: + client_id: "{{ scale_computing_hypercore_cluster_config.oidc.client_id }}" + shared_secret: "{{ scale_computing_hypercore_cluster_config.oidc.shared_secret | default(omit) }}" + certificate: "{{ scale_computing_hypercore_cluster_config.oidc.certificate | default(omit) }}" + config_url: "{{ scale_computing_hypercore_cluster_config.oidc.config_url }}" + scopes: "{{ scale_computing_hypercore_cluster_config.oidc.scopes }}" + when: scale_computing_hypercore_cluster_config.oidc | default(False) + +- name: Set time server + scale_computing.hypercore.time_server: + source: "{{ scale_computing_hypercore_cluster_config.time_server }}" + when: scale_computing_hypercore_cluster_config.time_server | default(False) + +- name: Set time zone + scale_computing.hypercore.time_zone: + zone: "{{ scale_computing_hypercore_cluster_config.time_zone }}" + when: scale_computing_hypercore_cluster_config.time_zone | default(False) + +- name: Set SMTP configuration + scale_computing.hypercore.smtp: + server: "{{ scale_computing_hypercore_cluster_config.smtp.server }}" + port: "{{ scale_computing_hypercore_cluster_config.smtp.port }}" + use_ssl: "{{ scale_computing_hypercore_cluster_config.smtp.use_ssl | default(omit) }}" + auth_user: "{{ scale_computing_hypercore_cluster_config.smtp.auth_user | default(omit) }}" + auth_password: "{{ scale_computing_hypercore_cluster_config.smtp.auth_password | default(omit) }}" + from_address: "{{ scale_computing_hypercore_cluster_config.smtp.from_address | default(omit) }}" + when: scale_computing_hypercore_cluster_config.smtp | default(False) + +- name: Reconfigure email alert recipients + when: + - '"email_alerts" in scale_computing_hypercore_cluster_config' + - | + scale_computing_hypercore_cluster_config.email_alerts or + scale_computing_hypercore_cluster_config.email_alerts == [] + block: + - name: Get old email alert recipients + scale_computing.hypercore.email_alert_info: + register: email_alert_info_result + + - name: Remove old email alerts + scale_computing.hypercore.email_alert: + email: "{{ email_alert_recipient.email }}" + state: absent + loop: "{{ email_alert_info_result.records }}" + loop_control: + loop_var: email_alert_recipient + when: email_alert_recipient.email not in scale_computing_hypercore_cluster_config.email_alerts + + - name: Set new email alerts + scale_computing.hypercore.email_alert: + email: "{{ email_address }}" + state: present + loop: "{{ scale_computing_hypercore_cluster_config.email_alerts or [] }}" + loop_control: + loop_var: email_address + +- name: Reconfigure syslog servers + when: + - '"syslog_servers" in scale_computing_hypercore_cluster_config' + - | + scale_computing_hypercore_cluster_config.syslog_servers or + scale_computing_hypercore_cluster_config.syslog_servers == [] + block: + - name: Get old syslog servers + scale_computing.hypercore.syslog_server_info: + register: syslog_server_info_result + + - name: Remove old syslog servers + scale_computing.hypercore.syslog_server: + host: "{{ syslog_server.host }}" + state: absent + loop: "{{ syslog_server_info_result.records }}" + loop_control: + loop_var: syslog_server + when: syslog_server.host not in (scale_computing_hypercore_cluster_config.syslog_servers | map(attribute='host') | list) + + - name: Set new syslog servers + scale_computing.hypercore.syslog_server: + host: "{{ syslog_server.host }}" + port: "{{ syslog_server.port | default(omit) }}" + protocol: "{{ syslog_server.protocol | default(omit) }}" + state: present + loop: "{{ scale_computing_hypercore_cluster_config.syslog_servers or [] }}" + loop_control: + loop_var: syslog_server diff --git a/tests/integration/targets/role_cluster_config/tasks/apply_and_test.yml b/tests/integration/targets/role_cluster_config/tasks/apply_and_test.yml new file mode 100644 index 000000000..ab2307980 --- /dev/null +++ b/tests/integration/targets/role_cluster_config/tasks/apply_and_test.yml @@ -0,0 +1,71 @@ +--- +# input: cluster_config_applied, cluster_config_expected + +- name: Apply cluster_config - {{ dbg_suffix }} + include_role: + name: scale_computing.hypercore.cluster_config + vars: + scale_computing_hypercore_cluster_config: "{{ cluster_config_applied }}" + +# check config is applied, for each setting +- name: Retrieve cluster_info - {{ dbg_suffix }} + scale_computing.hypercore.cluster_info: + register: cluster_info_result + +- name: Retrieve registration_info - {{ dbg_suffix }} + scale_computing.hypercore.registration_info: + register: registration_info_result + +- name: Retrieve DNS config - {{ dbg_suffix }} + scale_computing.hypercore.dns_config_info: + register: dns_config_info_result + +- name: Retrieve OIDC config - {{ dbg_suffix }} + scale_computing.hypercore.oidc_config_info: + register: oidc_config_info_result + +- name: Retrieve time_server - {{ dbg_suffix }} + scale_computing.hypercore.time_server_info: + register: time_server_info_result + +- name: Retrieve time_zone - {{ dbg_suffix }} + scale_computing.hypercore.time_zone_info: + register: time_zone_info_result + +- name: Retrieve SMTP configuration - {{ dbg_suffix }} + scale_computing.hypercore.smtp_info: + register: smtp_info_result + +- name: Retrieve email alert configuration - {{ dbg_suffix }} + scale_computing.hypercore.email_alert_info: + register: email_alert_info_result + +- name: Retrieve syslog servers configuration - {{ dbg_suffix }} + scale_computing.hypercore.syslog_server_info: + register: syslog_server_info_result + +- name: Check state - {{ dbg_suffix }} + ansible.builtin.assert: + that: + - cluster_info_result.record.name == cluster_config_expected.name + - registration_info_result.record.company_name == cluster_config_expected.registration.company_name + - registration_info_result.record.contact == cluster_config_expected.registration.contact + - registration_info_result.record.email == cluster_config_expected.registration.email + - registration_info_result.record.phone == cluster_config_expected.registration.phone + - dns_config_info_result.record.server_ips == cluster_config_expected.dns.server_ips + - dns_config_info_result.record.search_domains == cluster_config_expected.dns.search_domains + - oidc_config_info_result.record.client_id == cluster_config_expected.oidc.client_id + - oidc_config_info_result.record.config_url == cluster_config_expected.oidc.config_url + - oidc_config_info_result.record.scopes == cluster_config_expected.oidc.scopes + - time_server_info_result.record.host == cluster_config_expected.time_server + - time_zone_info_result.record.zone == cluster_config_expected.time_zone + - smtp_info_result.record.server == cluster_config_expected.smtp.server + - smtp_info_result.record.port == cluster_config_expected.smtp.port + - smtp_info_result.record.use_ssl == cluster_config_expected.smtp.use_ssl | default(False) + - smtp_info_result.record.auth_user == cluster_config_expected.smtp.auth_user | default("") + - smtp_info_result.record.auth_password == "" # auth_password is always returned as "" by HC3 API + - smtp_info_result.record.from_address == cluster_config_expected.smtp.from_address + - email_alert_info_result.records | map(attribute='email') | list | sort == cluster_config_expected.email_alerts | sort + - syslog_server_info_result.records | map(attribute='host') | list | sort == cluster_config_expected.syslog_servers | map(attribute='host') | list | sort + - syslog_server_info_result.records | map(attribute='port') | list | sort == cluster_config_expected.syslog_servers | map(attribute='port') | list | sort + - syslog_server_info_result.records | map(attribute='protocol') | list | sort == cluster_config_expected.syslog_servers | map(attribute='protocol') | list | sort diff --git a/tests/integration/targets/role_cluster_config/tasks/main.yml b/tests/integration/targets/role_cluster_config/tasks/main.yml new file mode 100644 index 000000000..c5ec6fe81 --- /dev/null +++ b/tests/integration/targets/role_cluster_config/tasks/main.yml @@ -0,0 +1,249 @@ +--- +- environment: + SC_HOST: "{{ sc_host }}" + SC_USERNAME: "{{ sc_username }}" + SC_PASSWORD: "{{ sc_password }}" + SC_TIMEOUT: "{{ sc_timeout }}" + + vars: + # values from integration_config.yml, after test we revert cunfiguration to them + sc_host_config: "{{ sc_config[sc_host] }}" + + cluster_config_a: + name: cluster-a + registration: + company_name: New company a + contact: John Smith a + phone: 056789000 + email: john_smith_a@gmail.com + dns: + server_ips: + - 1.1.1.1 + - 1.0.0.1 + search_domains: + - subdomain_1.example.com + - subdomain_2.example.com + oidc: + client_id: 12345a + shared_secret: secret_stuff_a + # certificate: plain_text_from_x509 + config_url: https://login.microsoftonline.com/76d4c62a-a9ca-4dc2-9187-e2cc4d9abe7f/v2.0/.well-known/openid-configuration + scopes: openid+profile + time_server: 2.pool.ntp.org + time_zone: Europe/Ljubljana + smtp: + server: mail_a.example.com + port: 25 + use_ssl: False + auth_user: "" + auth_password: "" + from_address: "ci-test-a@example.com" + email_alerts: + - user_a1@test.com + - user_a2@test.com + - user_c@test.com + syslog_servers: + - host: 10.10.10.10 + port: 42 + protocol: tcp + - host: 10.10.10.100 + port: 100 + protocol: tcp + # maybe: setup also users, and SSL-cert. + + cluster_config_b: + name: cluster-b + registration: + company_name: New company b + contact: John Smith b + phone: 056789111 + email: john_smith_b@gmail.com + dns: + server_ips: + - 8.8.8.8 + - 8.8.4.4 + search_domains: + - subdomain_3.example.com + - subdomain_4.example.com + oidc: + client_id: 12345b + shared_secret: secret_stuff_b + # certificate: plain_text_from_x509 + config_url: https://login.microsoftonline.com/76d4c62a-a9ca-4dc2-9187-e2cc4d9abe7f/v2.0/.well-known/openid-configuration + scopes: openid+profile + time_server: 3.pool.ntp.org + time_zone: Europe/Zagreb + smtp: + server: mail_b.example.com + port: 25 + use_ssl: True + auth_user: "smtp_user_b" + auth_password: "smtp_password_b" + from_address: "ci-test-b@example.com" + email_alerts: + - user_b2@test.com + - user_b1@test.com + - user_c@test.com + syslog_servers: + - host: 10.10.10.11 + port: 43 + protocol: udp + - host: 10.10.10.100 + port: 101 + protocol: udp + + # TODO test with invalid inputs: + # email_alerts - a single string, not list with length=1 + + # special values, they will set cluster to 'unconfigured' state. + cluster_config_empty: + # name: - cannot be deleted + # registration: - call module with state=absent + dns: + server_ips: [] + search_domains: [] + # oidc_config: - cannot be deleted + # time_server: - call module with state=absent + # time_zone: - call module with state=absent + # smtp_config: - cannot be deleted + email_alerts: [] + syslog_servers: [] + + # special values, they will not modify cluster - no known key in the dict + cluster_config_nochange_1: {} + # all 1st level keys intentionally missing + cluster_config_nochange_2: + # all 2nd level keys intentionally missing + dns: + cluster_config_nochange_3: + # 1st level keys present, they contain a no-change value (None) + name: + registration: + dns: + oidc: + time_server: + time_zone: + smtp: + email_alerts: + syslog_servers: + + cluster_config_nochange_4: + # 2nd level keys present, they contain a no-change value (None) + dns: + server_ips: + search_domains: + + block: + # ==================================================================================== + # ----------------------------------------------------------------- + # Apply cluster config A and test + - name: Apply and test cluster_config_a + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_a" + cluster_config_applied: "{{ cluster_config_a }}" + cluster_config_expected: "{{ cluster_config_a }}" + + # ----------------------------------------------------------------- + # Apply cluster_config_nochange and test + - name: Apply and test cluster_config_nochange_1 a + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_1 - a" + cluster_config_applied: "{{ cluster_config_nochange_1 }}" + cluster_config_expected: "{{ cluster_config_a }}" + + - name: Apply and test cluster_config_nochange_2 a + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_2 - a" + cluster_config_applied: "{{ cluster_config_nochange_2 }}" + cluster_config_expected: "{{ cluster_config_a }}" + + - name: Apply and test cluster_config_nochange_3 a + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_3 - a" + cluster_config_applied: "{{ cluster_config_nochange_3 }}" + cluster_config_expected: "{{ cluster_config_a }}" + + - name: Apply and test cluster_config_nochange_4 a + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_4 - a" + cluster_config_applied: "{{ cluster_config_nochange_4 }}" + cluster_config_expected: "{{ cluster_config_a }}" + + # ----------------------------------------------------------------- + # Apply cluster_config_empty and test + - name: Apply and test cluster_config_empty + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_empty - a" + cluster_config_applied: "{{ cluster_config_empty }}" + # we expect old configuration (cluster_config_a) to be partially replaced by empty values from cluster_config_empty + cluster_config_expected: "{{ cluster_config_a | combine(cluster_config_empty) }}" + + # ==================================================================================== + # ----------------------------------------------------------------- + # Apply cluster config B and test + - name: Apply and test cluster_config_b + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_b" + cluster_config_applied: "{{ cluster_config_b }}" + cluster_config_expected: "{{ cluster_config_b }}" + + # ----------------------------------------------------------------- + # Apply cluster_config_nochange and test + - name: Apply and test cluster_config_nochange_1 b + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_1 - b" + cluster_config_applied: "{{ cluster_config_nochange_1 }}" + cluster_config_expected: "{{ cluster_config_b }}" + + - name: Apply and test cluster_config_nochange_2 b + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_nochange_2 - b" + cluster_config_applied: "{{ cluster_config_nochange_2 }}" + cluster_config_expected: "{{ cluster_config_b }}" + + # ==================================================================================== + # ----------------------------------------------------------------- + # Partially apply cluster config A and test + # Start with empty cluster_config_partial, then gradually change it to cluster_config_a, one attribute at a time. + - ansible.builtin.set_fact: + cluster_config_expected: "{{ cluster_config_b }}" + + - ansible.builtin.include_tasks: partial_update.yml + vars: + cluster_config_partial_update: "{{ {item.key: item.value} }}" + loop: "{{ cluster_config_a | dict2items }}" + + # ==================================================================================== + # Revert to configuration from integration_config.yml + - ansible.builtin.include_tasks: revert_cluster_configuration.yml + vars: + scale_computing_hypercore_cluster_config: + name: "{{ sc_host_config.cluster.name }}" + registration: "{{ sc_host_config.registration }}" + dns: + server_ips: "{{ sc_host_config.dns_config.dns_servers }}" + search_domains: "{{ sc_host_config.dns_config.search_domains }}" + oidc: + client_id: "{{ sc_host_config.oidc.client_id_default }}" + shared_secret: "{{ sc_host_config.oidc.shared_secret_default }}" + # certificate: "{{ sc_host_config.oidc.certificate | default(omit) }}" + config_url: "{{ sc_host_config.oidc.config_url_default }}" + scopes: "{{ sc_host_config.oidc.scopes }}" + time_server: "{{ sc_host_config.time_server.source }}" + time_zone: "{{ sc_host_config.time_zone.zone }}" + smtp: "{{ sc_host_config.smtp }}" + email_alerts: "{{ sc_host_config.email_alert }}" + # integration_config.yml contains a single syslog server + syslog_servers: + - host: "{{ sc_host_config.syslog_server.host }}" + port: 514 + protocol: udp diff --git a/tests/integration/targets/role_cluster_config/tasks/partial_update.yml b/tests/integration/targets/role_cluster_config/tasks/partial_update.yml new file mode 100644 index 000000000..bb154fa57 --- /dev/null +++ b/tests/integration/targets/role_cluster_config/tasks/partial_update.yml @@ -0,0 +1,15 @@ +--- +# input: cluster_config_expected, cluster_config_partial_update +# cluster_config_partial_update is applied to cluster +# cluster_config_expected is updated with cluster_config_partial_update, and this +# is expected state on the server + +- ansible.builtin.set_fact: + cluster_config_expected: "{{ cluster_config_expected | combine(cluster_config_partial_update) }}" + +- name: Apply and test cluster_config_partial_update + include_tasks: apply_and_test.yml + vars: + dbg_suffix: "cluster_config_partial_update={{ cluster_config_partial_update }}" + cluster_config_applied: "{{ cluster_config_partial_update }}" + # cluster_config_expected: "{{ cluster_config_expected }}" # is a global var/fact diff --git a/tests/integration/targets/role_cluster_config/tasks/revert_cluster_configuration.yml b/tests/integration/targets/role_cluster_config/tasks/revert_cluster_configuration.yml new file mode 100644 index 000000000..964086a7f --- /dev/null +++ b/tests/integration/targets/role_cluster_config/tasks/revert_cluster_configuration.yml @@ -0,0 +1,105 @@ +# Revert to configuration from integration_config.yml +# This is basically copy of role main.yml. +# Stupid. But if we break tested role, the revert logic should still work. +# So we copy-paste. +# This also allows to have different variable structure in integration_config.yml + +- name: REVERT Set cluster name + scale_computing.hypercore.cluster_name: + name_new: "{{ scale_computing_hypercore_cluster_config.name }}" + when: scale_computing_hypercore_cluster_config.name | default(False) + +- name: REVERT Set registration data + scale_computing.hypercore.registration: + company_name: "{{ scale_computing_hypercore_cluster_config.registration.company_name }}" + contact: "{{ scale_computing_hypercore_cluster_config.registration.contact }}" + phone: "{{ scale_computing_hypercore_cluster_config.registration.phone }}" + email: "{{ scale_computing_hypercore_cluster_config.registration.email }}" + state: present + +- name: REVERT Set DNS configuration + scale_computing.hypercore.dns_config: + dns_servers: "{{ scale_computing_hypercore_cluster_config.dns.server_ips | default(omit) }}" + search_domains: "{{ scale_computing_hypercore_cluster_config.dns.search_domains | default(omit) }}" + state: set + +- name: REVERT Set OIDC configuration + scale_computing.hypercore.oidc_config: + client_id: "{{ scale_computing_hypercore_cluster_config.oidc.client_id }}" + shared_secret: "{{ scale_computing_hypercore_cluster_config.oidc.shared_secret | default(omit) }}" + certificate: "{{ scale_computing_hypercore_cluster_config.oidc.certificate | default(omit) }}" + config_url: "{{ scale_computing_hypercore_cluster_config.oidc.config_url }}" + scopes: "{{ scale_computing_hypercore_cluster_config.oidc.scopes }}" + +- name: REVERT Set time server + scale_computing.hypercore.time_server: + source: "{{ scale_computing_hypercore_cluster_config.time_server }}" + +# Oh no. "US/Estern" iz not accepted by time_zone modules. We need to fix it. +# Revert with api modele +#- name: REVERT Set time zone +# scale_computing.hypercore.time_zone: +# zone: "{{ scale_computing_hypercore_cluster_config.time_zone }}" +- name: REVERT Set time zone using api module + scale_computing.hypercore.api: + action: post + endpoint: /rest/v1/TimeZone/timezone_guid + data: + timeZone: "{{ scale_computing_hypercore_cluster_config.time_zone }}" + +- name: REVERT Set SMTP configuration + scale_computing.hypercore.smtp: + server: "{{ scale_computing_hypercore_cluster_config.smtp.host }}" + port: "{{ scale_computing_hypercore_cluster_config.smtp.port }}" + use_ssl: "{{ scale_computing_hypercore_cluster_config.smtp.use_ssl | default(omit) }}" + auth_user: "{{ scale_computing_hypercore_cluster_config.smtp.auth_user | default(omit) }}" + auth_password: "{{ scale_computing_hypercore_cluster_config.smtp.auth_password | default(omit) }}" + from_address: "{{ scale_computing_hypercore_cluster_config.smtp.from_address | default(omit) }}" + +- name: REVERT Reconfigure email alert recipients + block: + - name: REVERT Get old email alert recipients + scale_computing.hypercore.email_alert_info: + register: email_alert_info_result + + - name: REVERT Remove old email alerts + scale_computing.hypercore.email_alert: + email: "{{ email_alert_recipient.email }}" + state: absent + loop: "{{ email_alert_info_result.records }}" + loop_control: + loop_var: email_alert_recipient + when: email_alert_recipient.email not in scale_computing_hypercore_cluster_config.email_alerts + + - name: REVERT Set new email alerts + scale_computing.hypercore.email_alert: + email: "{{ email_address }}" + state: present + loop: "{{ scale_computing_hypercore_cluster_config.email_alerts }}" + loop_control: + loop_var: email_address + +- name: REVERT Reconfigure syslog servers + block: + - name: REVERT Get old syslog servers + scale_computing.hypercore.syslog_server_info: + register: syslog_server_info_result + + - name: REVERT Remove old syslog servers + scale_computing.hypercore.syslog_server: + host: "{{ syslog_server.host }}" + state: absent + loop: "{{ syslog_server_info_result.records }}" + loop_control: + loop_var: syslog_server + when: syslog_server.host not in (scale_computing_hypercore_cluster_config.syslog_servers | map(attribute='host') | list) + + - name: REVERT Set new syslog servers + scale_computing.hypercore.syslog_server: + host: "{{ syslog_server.host }}" + port: "{{ syslog_server.port | default(omit) }}" + protocol: "{{ syslog_server.protocol | default(omit) }}" + state: present + loop: "{{ scale_computing_hypercore_cluster_config.syslog_servers }}" + loop_control: + loop_var: syslog_server