diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/sensu/sensu_go/tests | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/sensu/sensu_go/tests')
181 files changed, 15687 insertions, 0 deletions
diff --git a/ansible_collections/sensu/sensu_go/tests/config.yml b/ansible_collections/sensu/sensu_go/tests/config.yml new file mode 100644 index 000000000..59769d374 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/config.yml @@ -0,0 +1,3 @@ +--- +modules: + python_requires: ">= 2.7" diff --git a/ansible_collections/sensu/sensu_go/tests/integration/base.yml b/ansible_collections/sensu/sensu_go/tests/integration/base.yml new file mode 100644 index 000000000..396c4ebfe --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/base.yml @@ -0,0 +1,54 @@ +--- +scenario: + test_sequence: + - destroy + - create + - converge + - destroy +dependency: + name: galaxy +driver: + name: docker +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + lint: + enabled: false +platforms: + - name: v6.9.0 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.9.0 + pre_build_image: true + pull: true + override_command: false + - name: v6.8.2 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.8.2 + pre_build_image: true + pull: true + override_command: false + - name: v6.7.5 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.7.5 + pre_build_image: true + pull: true + override_command: false + - name: v6.6.2 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.6.2 + pre_build_image: true + pull: true + override_command: false + - name: v6.5.5 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.5.5 + pre_build_image: true + pull: true + override_command: false + - name: v6.4.3 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.4.3 + pre_build_image: true + pull: true + override_command: false + - name: v6.3.0 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.3.0 + pre_build_image: true + pull: true + override_command: false diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml new file mode 100644 index 000000000..ceeb3bdcd --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml @@ -0,0 +1,102 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a Bonsai asset + bonsai_asset: &idempotence + auth: + url: http://localhost:8080 + name: sensu/monitoring-plugins + version: 2.2.0-1 + on_remote: true + register: result + + - assert: + that: + # https://bonsai.sensu.io/api/v1/assets/sensu/monitoring-plugins/2.2.0-1/release_asset_builds + - result is changed + - result.object.metadata.name == 'sensu/monitoring-plugins' + - result.object.builds | length == 3 + - result.object.metadata.annotations | dict2items | length == 7 + + - name: Test asset creation idempotence + bonsai_asset: *idempotence + register: result + + - assert: + that: result is not changed + + - name: Modify an asset + bonsai_asset: + auth: + url: http://localhost:8080 + name: sensu/monitoring-plugins + version: 2.2.0-2 + labels: + my: label + annotations: + anot: here + register: result + + - assert: + that: + # https://bonsai.sensu.io/api/v1/assets/sensu/monitoring-plugins/2.2.0-2/release_asset_builds + - result is changed + - result.object.metadata.name == 'sensu/monitoring-plugins' + - result.object.builds | length == 4 + - result.object.metadata.annotations | dict2items | length == 8 + - result.object.metadata.annotations.anot == 'here' + - result.object.metadata.labels.my == 'label' + - result is changed + + - name: Fetch a specific asset + asset_info: + auth: + url: http://localhost:8080 + name: sensu/monitoring-plugins + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'sensu/monitoring-plugins' + + - name: Add same asset under different name + bonsai_asset: + auth: + url: http://localhost:8080 + name: sensu/monitoring-plugins + version: 2.2.0-2 + rename: renamed-asset + + - name: Fetch renamed asset + asset_info: + auth: + url: http://localhost:8080 + name: renamed-asset + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'renamed-asset' + + - name: Delete an asset + asset: + auth: + url: http://localhost:8080 + name: sensu/monitoring-plugins + state: absent + + - name: Fetch all assets again after deletion + asset_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 1 diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml new file mode 100644 index 000000000..5cc1fb19f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml @@ -0,0 +1,313 @@ +--- +- name: Prepare new backend + hosts: has_api_key_support + gather_facts: no + + tasks: + - name: Create a regular user + sensu.sensu_go.user: + name: test_user + password: test_pass + + - name: Create a regular user role + sensu.sensu_go.role: + name: test_role + rules: + - verbs: [ list ] + resources: [ assets ] + + - name: Allow user to list entities + sensu.sensu_go.role_binding: + name: test_binding + role: test_role + users: [ test_user ] + + - name: Configure sensuctl + command: + cmd: > + sensuctl configure + --non-interactive + --url http://localhost:8080 + --username admin + --password P@ssw0rd! + --namespace default + + - name: Create API key for admin + command: + cmd: sensuctl api-key grant admin + register: api_key_admin_result + + - name: Create API key for test user + command: + cmd: sensuctl api-key grant test_user + register: api_key_test_user_result + + - name: Store API keys + set_fact: + api_key_admin: "{{ api_key_admin_result.stdout | regex_search('[^/]+$') }}" + api_key_test_user: "{{ api_key_test_user_result.stdout | regex_search('[^/]+$') }}" + + - name: Change default admin password for tests + command: + cmd: > + sensuctl user change-password admin + --current-password 'P@ssw0rd!' + --new-password insecure + + +- name: Test against a new backend + hosts: has_api_key_support + collections: + - sensu.sensu_go + gather_facts: no + + tasks: + - name: Fail with invalid password + mutator: + auth: + user: admin + password: not-a-password + url: http://localhost:8080 + name: my_mutator + command: sensu-influxdb-mutator + timeout: 30 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with bad username + asset_info: + auth: + user: not-a-valid-user + password: insecure + url: http://localhost:8080 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with bad API token + hook: + auth: + api_key: not-a-valid-api-key + url: http://localhost:8080 + name: restart_nginx + command: sudo systemctl start nginx + timeout: 60 + stdin: false + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with bad API token even if username/password would be OK + hook: + auth: + api_key: not-a-valid-api-key + user: admin + password: insecure + url: http://localhost:8080 + name: restart_nginx + command: sudo systemctl start nginx + timeout: 60 + stdin: false + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Authenticate with a valid user/pass combination + asset: + auth: + user: admin + password: insecure + url: http://localhost:8080 + name: my_asset + builds: + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b + register: result + + - assert: + that: + - result is success + + - name: Authenticate with a valid API key + asset_info: + auth: + api_key: "{{ api_key_admin }}" + url: http://localhost:8080 + register: result + + - assert: + that: + - result is success + + - name: Authenticate with a valid API key and ignore user/password + asset_info: + auth: + api_key: "{{ api_key_admin }}" + user: not-a-valid-user + password: not-a-password + url: http://localhost:8080 + register: result + + - assert: + that: + - result is success + + - name: List assets using test user API key + asset_info: + auth: + api_key: "{{ api_key_test_user }}" + + - name: Fail to create an asset because test_user can only list them + bonsai_asset: + auth: + api_key: "{{ api_key_test_user }}" + name: sensu/monitoring-plugins + version: 2.2.0-1 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + + - name: Fail to list entities because test_user has no access to entities + entity_info: + auth: + api_key: "{{ api_key_test_user }}" + ignore_errors: true + register: result + + - assert: + that: + - result is failed + + +- name: Prepare old backend + hosts: no_api_key_support + gather_facts: no + + tasks: + - name: Configure sensuctl + command: + cmd: > + sensuctl configure + --non-interactive + --url http://localhost:8080 + --username admin + --password P@ssw0rd! + --namespace default + + - name: Change default admin password for tests + command: + cmd: > + sensuctl user change-password admin + --current-password 'P@ssw0rd!' + --new-password insecure + + +- name: Test against an old backend + hosts: no_api_key_support + collections: + - sensu.sensu_go + gather_facts: no + + tasks: + - name: Fail with invalid password + mutator: + auth: + user: admin + password: not-a-password + url: http://localhost:8080 + name: my_mutator + command: sensu-influxdb-mutator + timeout: 30 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with bad username + asset_info: + auth: + user: not-a-valid-user + password: insecure + url: http://localhost:8080 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with API token + hook: + auth: + api_key: not-a-valid-api-key + url: http://localhost:8080 + name: restart_nginx + command: sudo systemctl start nginx + timeout: 60 + stdin: false + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Fail with bad API token even if username/password would be OK + hook: + auth: + api_key: not-a-valid-api-key + user: admin + password: insecure + url: http://localhost:8080 + name: restart_nginx + command: sudo systemctl start nginx + timeout: 60 + stdin: false + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "'Authentication' in result.msg" + + - name: Authenticate with a valid user/pass combination + asset: + auth: + user: admin + password: insecure + url: http://localhost:8080 + name: my_asset + builds: + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b + register: result + + - assert: + that: + - result is success diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml new file mode 100644 index 000000000..1648fc459 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml @@ -0,0 +1,13 @@ +platforms: + - name: v6.4.1 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.4.1 + groups: [ has_api_key_support ] + pre_build_image: true + pull: true + override_command: false + - name: v5.14.2 + image: quay.io/xlab-steampunk/sensu-go-tests-sensu:5.14.2 + groups: [ no_api_key_support ] + pre_build_image: true + pull: true + override_command: false diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml new file mode 100644 index 000000000..a0867bb32 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml @@ -0,0 +1,67 @@ +--- +- name: Converge + hosts: all + gather_facts: no + + tasks: + - name: Test connection using no verification + sensu.sensu_go.asset_info: + auth: + url: https://localhost:8080 + verify: false + + - name: Test connection using no verification (env var) + sensu.sensu_go.asset_info: + auth: + url: https://localhost:8080 + environment: + SENSU_VERIFY: "false" + + - name: Test connection using custom CA + sensu.sensu_go.user_info: + auth: + url: https://sensu-api:8080 + ca_path: /etc/sensu/api-ca.crt + + - name: Test connection using custom CA (env var) + sensu.sensu_go.user_info: + auth: + url: https://sensu-api:8080 + environment: + SENSU_CA_PATH: /etc/sensu/api-ca.crt + + - name: Test connection using default verification + sensu.sensu_go.asset_info: + auth: + url: https://localhost:8080 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - '"certificate" in result.msg' + + - name: Test connection using custom CA not matching name + sensu.sensu_go.user_info: + auth: + url: https://localhost:8080 + ca_path: /etc/sensu/api-ca.crt + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - result.msg is search("localhost.+sensu-api") + + - name: Test connection using the wrong protocol + sensu.sensu_go.asset_info: + auth: + url: http://localhost:8080 + ignore_errors: true + register: result + + - assert: + that: + - result is failed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh new file mode 100755 index 000000000..eb5509321 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Generate root CA certificate +openssl genrsa -out sensu-api-ca.key 2048 +openssl req -x509 -sha256 -new -nodes -key sensu-api-ca.key -subj '/CN=Sensu-test CA' -days 1095 -out sensu-api-ca.crt + +# Generate certificate +openssl genrsa -out sensu-api.key 2048 +openssl req -new -key "sensu-api.key" -out "sensu-api.csr" -sha256 -subj '/CN=sensu-api' +openssl x509 -req -days 1095 -in "sensu-api.csr" -sha256 -CA "sensu-api-ca.crt" -CAkey "sensu-api-ca.key" -CAcreateserial -out "sensu-api.crt" -extfile "sensu-api.cnf" -extensions server + +# print content of certificate +openssl x509 -in sensu-api-ca.crt -text +openssl x509 -in sensu-api.crt -text
\ No newline at end of file diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt new file mode 100644 index 000000000..a2301e4b9 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIUBSnmUh6PCEF3/6zTJ1DqH9rcNuswDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMTIwNjQ3NDdaFw0y +NTEyMTEwNjQ3NDdaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoAXA7t1aC4zNgZNCasPHxcyCfmC4J5zoh +oTstyiFxhNv131EEQtpIABcWsgDXtQozJuAgHTVuGHEbHlFu7BFrh9Ik6FEznuZW +pZ53V//LRtQZt7DLQrJP2ZJhGedSm+exZj8f0Uk8do0xI46BtwT/u5pzkLN8NLCk +z+xu2mnFN+pdTqJLHP59JpRV1E0Al2E1m9rCfR8grGh4lTFDE/SwUueaHmg5Z0TN +QuROuRii1HcK8g7QZ0HyOWRGfpNGzXq2kYUaGTvONrSwGe4bPjT/Jp4TElUV9mSA +YoBE6JaDi1v28sXD9Cz6JgK63Ci0lmz6ny3H7kNfMQECgpj3KWL7AgMBAAGjUzBR +MB0GA1UdDgQWBBQrUp514z1VOH+uWKjmtkvomGULLjAfBgNVHSMEGDAWgBQrUp51 +4z1VOH+uWKjmtkvomGULLjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCg/Po64hN09w4QxwLvFRo050NBpvTi4JHrC/xQjFWl60UlQ+tCICtKDRRA +MCpOt+PqJlivm8sDrSn86vqQKMJfQHGUDExNpcEWFG+QLUJ0aX5jFUj6+V+2hAbO +g3QR9OvqeewuIfpHQJ8CTEbSamhc03LC2eX0AqdQUwSMuNrNgrZ0BBVTMkEfx8fM +vfhWPjbO7WDjj0NJrh/FRXxIplvog1vGVY5RNRNtGgs3hGwh+roNq0kxOTO+Ye5D +rtFSQ9CfjZI9qsT+XA8R6WzvFqSZSuxQpPkh70Pl6TPlK/W4Pqec2izZSlJxKFp1 +/rHU0nyXHJ2d7X5iV/sd3/WTs8kV +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf new file mode 100644 index 000000000..1aec86d31 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf @@ -0,0 +1,9 @@ +[server] +basicConstraints=CA:FALSE +subjectKeyIdentifier=hash +authorityKeyIdentifier=issuer + +extendedKeyUsage=serverAuth +keyUsage=digitalSignature, keyEncipherment +subjectAltName=IP:127.0.0.1 + diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt new file mode 100644 index 000000000..f2fdc951f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIUBvuM6lXj9IVDxOUs7Szg6+vzEu8wDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMTIwNzE1MzRaFw0y +NTEyMTEwNzE1MzRaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAOHOCPfinziVMh0dJljYnnJ7/H6+Tj6fbf+I01U8 +hRQLhhKoA6n51uuThS6ncBiWhyC6t09i/SbXqvybdIVrn1Gcj/yyDpeTDxKs3GHY +i7q3xeDkJ/qWwsBK7sGtN3F6T/qQUvnhMhG8DzsNH+G11G3PnOJD6CMwmsCzFqip +GmagDFUOleHaMDaTZOiT6C8Bv+w08KakGuRoKtJUlIJCps9Za1ZnpcRKv5nY7Mxo +K1/K1M9dDmznuh0nHkSVjBig5j3I3k4Fto4ApIN/eGHoyCc9uXHCHoB8K7xwGeNQ +eNuxzYUmryemA51p/AjtIIXe7Hn9XxkTmWolCiv14Prp/ZUCAwEAAaOBnzCBnDAJ +BgNVHRMEAjAAMB0GA1UdDgQWBBQ+DDirz38IyldwTg9p38Ti0wAytzA9BgNVHSME +NjA0oRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBghQFKeZSHo8IQXf/rNMn +UOof2tw26zATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDwYDVR0R +BAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAkXWRt3m4IGp/6+/icT5PCZ3M +S7FCzx6f+2BxxvGdFX0JI1HTjqO7cUHQ57lC/eRHyx15W6CxgDkUCZJ/tJot0Q+k +ZZPHUsO6mG+d/DdMyRbfzOtH0xSrIFZTXdG9gSfEGG+r5IX+TBcZ3BOHtIgRYJsK +p3elglhsSgChxZ6SlcSp52KK9gaFcBywLtIt13BpRm/F9JX2IKmX4yZIFwvMg5W9 +MmDdR1GSBDe/uS9yRhst3hVOxBBLX9J2P1xBf4TYgmIAPqLhSCKIdr0ciRwwnV0D +dOk8q2eCVLqgu+Hl/NJWqLvI99eRtya20uVV458ZMjVa1ugw4kcpU7UmOEZajg== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key new file mode 100644 index 000000000..efea0f675 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA4c4I9+KfOJUyHR0mWNiecnv8fr5OPp9t/4jTVTyFFAuGEqgD +qfnW65OFLqdwGJaHILq3T2L9Jteq/Jt0hWufUZyP/LIOl5MPEqzcYdiLurfF4OQn ++pbCwEruwa03cXpP+pBS+eEyEbwPOw0f4bXUbc+c4kPoIzCawLMWqKkaZqAMVQ6V +4dowNpNk6JPoLwG/7DTwpqQa5Ggq0lSUgkKmz1lrVmelxEq/mdjszGgrX8rUz10O +bOe6HSceRJWMGKDmPcjeTgW2jgCkg394YejIJz25ccIegHwrvHAZ41B427HNhSav +J6YDnWn8CO0ghd7sef1fGROZaiUKK/Xg+un9lQIDAQABAoIBAQCEUbwnpoEvIx6O +uPozrhyLceRwUQyA3eQTjhZpKGHDcU1LuXEMNf+fZH7y6+NgRTVCFKg+uP8nt9HW +3THWzU47AMfPiHfMkryOcQVjwQWAkRg/xPM4gQf2rvJiRCLtOIONjO1SyIgSpGU4 +cWRxW5/0CWkhnjF2DZFhwpBQnWd/IsiiCrqCrEgKm4yjgu63LVEkBRmSwzkms2qO +aWOBnut/DJNkMdV3Zaxb+MYkaYmrus6viPGk3brbt70qkuFzJh4l/KSZcCWHofUW +3Rs1nrgNmrzaDEscULNZTE/4WSL32tyXyTo8ujIqVQCgMXbWc1QujV1AyHL1EZep +KRSYpn6BAoGBAPkTc35qlOZSOBE/GeIddqFlNbNK0XaJhjH0lNc1L/aoxNLQCNqX +cUCu1CB2YVoERhghvuLUOULkeVt9LeJywpLYW7l3lLbqJoBWedRqEHAkVxGxARqO +KWwgMyFgphTrkgfmSlOYWrZ1hW8BPL84pwQbLgyRU6jzkmHi0Re/GbjZAoGBAOgU ++YgO47vqCfQhrinWYESsqH3OZGoAaIlRqEMv4vPIq3UHtWne1je9vGRCTCgdA+GJ +6tkIbx04m61FvPXW/HlWlSYvA4zafWZFKjWkNtsrOZBIknqtQ9h5ca1CsQjEfWLJ +WdMN5DMTNQeK6irNHFHXFJHejlig/dCOdwF7oFUdAoGAXsgbHBEc5mSFN3Lmu5fw +q8wi2j5vZQdCTMJA3YA850Uj2QEXTW9xxmaBDHVf6GxV7BrzU8fknmLpF3qUOmbn +ShARH4u0yMJjslS1+bH+3V3G0FGmFN/iPWYnbt0jdjSKlnz47cS9SE+CXlJ/Nlkt +nS9mn5ux1UoS+zLf1ISBGkkCgYAZ3XyR9VbcMpE5bCeE8id4f+WnX8FBLxp3c2pV +UpjwooS5XkqRqgwl2jeM1Pa34cP09vH9jjsT/qMbBJKys2sf+s5UmxjCMfeX1k2F +/O9ALekUc4Ifuf+9uIs6zBv+5iczQ02HgBzWtGVMmebPLlzhoh7gwvUKW6bt/3Kz +75tG4QKBgFz2h/AVPz52Fraztx7QUc2J9sgM4yERvZqVnVCYVINrczi7iV+LS+yE +x4CenPWPRgnoh4P2jmqybmw+3rGsk4UyTPYZhvM7FUUI5DsD/Quk9KYgDIQBQTxV +0ryPzC6j8uYweF9xLKwfv+fIj8j+iqcOwhXWd5+E2YOXOIdKS3Oa +-----END RSA PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem new file mode 100644 index 000000000..beff18dce --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIUAOVga11hbTPIxN+jFbDcB6YX/PwwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMDkxMzM5NThaFw0y +NTEyMDgxMzM5NThaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAJGoCboPEiiKj9E9YUUdVR3oIS1mHYQLqqd0T0Y5 +BTp4XbeLdvv/oFF5nol5QUWUJm7PHa8CUY6LO9k+fW2VkhQcBnrrv0H1e23DgyWm +J2pOjp54RYHciGErRnAkwpNt3Z6Q6oMnnK77hp5YiPsk3AvwgQaHFgOpI1L6Z2VI +difGuckMUruwVKT0q+y2zHDvewzf55J+ykBKVYjVEi8Kx/gaAIe12VybAyg5P0QM +ZUiKfcb7f/sae4KEZOWSXKfh8HaK/YXQN0JxwFujPt+MHxtUlzEyOakWgBjhP7nQ +BB8FgQmyE+m9xuq2bHei/qzx1183Pwd+zPrVoAWY+Rlv9x8CAwEAAaOBnzCBnDAJ +BgNVHRMEAjAAMB0GA1UdDgQWBBTqiIfWrKiwVZTFchGktuW8WWtFRDA9BgNVHSME +NjA0oRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBghQxv8cd6BODLyXX7rdB +udWKli0pBjATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDwYDVR0R +BAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEANptW8pIOrmulcqZ78n+b95zu +zcxoD0n1QjhouiEqlCgEaVoOpWbm12tds6pALDVuBNF1lYuTe2Kdffxoz/q7lpxb +Z07fU5lXF3mGa8CtsbXxlnpRxu1HIQwChp3EGxjxhOiGmfxfULicOl/z3nfWR9zu ++/7jpKRXAJ+O5JvxdU754cmXXftk+XncUz19ZbizX7trWDuqzkyTxD/0lNRA6OZN +hVF8FVE/Mju3FXbY4atvptV4e8MOsz1vfS6piPtU9HzD2miWuoY4di+OgC3DDTQs +4eDkOLvyoJ04JQMst928Oka6NPHnfUVxBeQg9JtzCDbFT/km4bQ4snybQ4vPnQ== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml new file mode 100644 index 000000000..1785c689f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml @@ -0,0 +1,20 @@ +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - destroy + +platforms: + - name: backend + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true + override_command: false + privileged: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + etc_hosts: + # sensu-api is the hostname used in the test certificate + sensu-api: 127.0.0.1 diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml new file mode 100644 index 000000000..1f1463259 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml @@ -0,0 +1,13 @@ +--- +- name: Prepare + hosts: all + + tasks: + - name: Install backend with secured API + include_role: + name: sensu.sensu_go.backend + vars: + version: 5.21.0 + api_cert_file: files/sensu-api.crt + api_key_file: files/sensu-api.key + api_trusted_ca_file: files/sensu-api-ca.crt diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml new file mode 100644 index 000000000..0112074b9 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml @@ -0,0 +1,360 @@ +--- +- name: Converge + hosts: all + gather_facts: no + tasks: + - name: Fetch all AD auth providers and verify the presence + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Fail when trying to create a AD auth provider with missing required params + sensu.sensu_go.ad_auth_provider: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + ignore_errors: true + + - ansible.builtin.assert: + that: + - result is failed + - "'state is present but all of the following are missing' in result.msg" + + - name: Create AD auth provider with minimal params (check mode) + sensu.sensu_go.ad_auth_provider: &create-provider + state: present + name: activedirectory + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + check_mode: true + register: result + + - ansible.builtin.assert: &create-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'activedirectory' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + + - name: Make sure AD auth provider was not created when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create AD auth provider with minimal params + sensu.sensu_go.ad_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: *create-provider-assertions + + - name: Make sure AD auth provider was created + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'activedirectory' + + - name: Idempotence check for AD auth provider creation with minimal params + sensu.sensu_go.ad_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update AD auth provider (check mode) + sensu.sensu_go.ad_auth_provider: &update-provider + state: present + name: activedirectory + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + groups_prefix: dev + check_mode: true + register: result + + - ansible.builtin.assert: &update-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'activedirectory' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.object.groups_prefix == 'dev' + + - name: Make sure AD auth provider was not updated in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'activedirectory' + - result.objects.0.servers.0.host == '127.0.0.1' + - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.groups_prefix == '' + + - name: Update AD auth provider + sensu.sensu_go.ad_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: *update-provider-assertions + + - name: Make sure AD auth provider was updated + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'activedirectory' + - result.objects.0.servers.0.host == '127.0.0.1' + - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.groups_prefix == 'dev' + + - name: Idempotence check for AD auth provider modification + sensu.sensu_go.ad_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Add AD auth provider server to existing one (check mode) + sensu.sensu_go.ad_auth_provider: &create-extra-provider + state: present + name: activedirectory + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + - host: 127.0.0.2 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + groups_prefix: dev + check_mode: true + register: result + + - ansible.builtin.assert: &create-extra-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'activedirectory' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.1.host == '127.0.0.2' + - result.object.servers.1.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.1.user_search.base_dn == 'dc=acme,dc=org' + + - name: Make sure extra AD auth provider was not created when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.servers | length == 1 + + - name: Create an extra AD auth provider + sensu.sensu_go.ad_auth_provider: *create-extra-provider + register: result + + - ansible.builtin.assert: *create-extra-provider-assertions + + - name: Make sure extra AD auth provider was created + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.servers | length == 2 + - result.objects.0.metadata.name == 'activedirectory' + + - name: Idempotence check for extra AD auth provider creation + sensu.sensu_go.ad_auth_provider: *create-extra-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create a AD auth provider with all params + sensu.sensu_go.ad_auth_provider: &create-provider-all-params + auth: + url: http://localhost:8080 + state: present + name: other-activedirectory + servers: + - host: 127.0.0.1 + port: 636 + insecure: false + security: tls + trusted_ca_file: /path/to/trusted-certificate-authorities.pem + client_cert_file: /path/to/ssl/cert.pem + client_key_file: /path/to/ssl/key.pem + default_upn_domain: example.org + binding: + user_dn: cn=binder,dc=acme,dc=org + password: ad_password + group_search: + base_dn: dc=acme,dc=org + attribute: member + name_attribute: cn + object_class: groupOfNames + user_search: + base_dn: dc=acme,dc=org + attribute: uid + name_attribute: cn + object_class: person + groups_prefix: dev + username_prefix: ad + register: result + + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == 'other-activedirectory' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.port == 636 + - result.object.servers.0.insecure == false + - result.object.servers.0.security == 'tls' + - result.object.servers.0.trusted_ca_file == '/path/to/trusted-certificate-authorities.pem' + - result.object.servers.0.client_cert_file == '/path/to/ssl/cert.pem' + - result.object.servers.0.client_key_file == '/path/to/ssl/key.pem' + - result.object.servers.0.default_upn_domain == 'example.org' + - result.object.servers.0.binding.user_dn == 'cn=binder,dc=acme,dc=org' + - "'password' not in result.object.servers.0.binding" + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.group_search.attribute == 'member' + - result.object.servers.0.group_search.name_attribute == 'cn' + - result.object.servers.0.group_search.object_class == 'groupOfNames' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.attribute == 'uid' + - result.object.servers.0.user_search.name_attribute == 'cn' + - result.object.servers.0.user_search.object_class == 'person' + - result.object.groups_prefix == 'dev' + - result.object.username_prefix == 'ad' + + - name: Idempotence check for AD auth provider creation with all params + sensu.sensu_go.ad_auth_provider: *create-provider-all-params + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Delete AD auth provider (check mode) + sensu.sensu_go.ad_auth_provider: &delete-provider + auth: + url: http://localhost:8080 + name: activedirectory + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure AD auth provider was not deleted when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete AD auth provider + sensu.sensu_go.ad_auth_provider: *delete-provider + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure AD auth provider was deleted + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: activedirectory + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Check if still any AD auth providers exist + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete AD auth provider + sensu.sensu_go.ad_auth_provider: + auth: + url: http://localhost:8080 + name: other-activedirectory + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Delete a non-existent AD auth provider + sensu.sensu_go.ad_auth_provider: + auth: + url: http://localhost:8080 + name: i-dont-exist + state: absent + register: result + + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml new file mode 100644 index 000000000..062ab7ee0 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml @@ -0,0 +1,260 @@ +--- +- name: Setup sensuctl + hosts: all + gather_facts: no + + tasks: + - name: Configure sensuctl + command: + cmd: > + sensuctl configure + --non-interactive + --url http://localhost:8080 + --username admin + --password P@ssw0rd! + --namespace default + + +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create an asset with missing required parameters + asset: + auth: + url: http://localhost:8080 + name: asset + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: builds'" + + - name: Create an asset with empty builds + asset: + auth: + url: http://localhost:8080 + name: asset + builds: + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'builds must include at least one element'" + + - name: Create an asset with missing parameters for build + asset: + auth: + url: http://localhost:8080 + name: asset + builds: + - url: http://assets.bonsai.sensu.io/asset + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'missing required arguments: sha512 found in builds'" + + - name: Create an asset with minimal parameters + asset: + auth: + url: http://localhost:8080 + name: minimal_asset + builds: + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b + register: result + + - assert: + that: + - result is changed + - result.object.builds | length == 1 + - result.object.builds.0.url == 'https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz' + - result.object.builds.0.sha512 == '518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b' + - result.object.builds.0.filters == None + - result.object.builds.0.headers == None + + - name: Create an asset + asset: + auth: + url: http://localhost:8080 + name: asset + builds: + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b + filters: + - entity.system.os == 'linux' + - entity.system.arch == 'amd64' + - entity.system.platform == 'rhel' + headers: + Sensu-Blivet: foo + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_alpine_linux_amd64.tar.gz + sha512: b2da25ecd7642e6de41fde37d674fe19dcb6ee3d680e145e32289f7cfc352e6b5f9413ee9b701d61faeaa47b399aa30b25885dbc1ca432c4061c8823774c28f3 + filters: + - entity.system.os == 'linux' + - entity.system.arch == 'amd64' + - entity.system.platform == 'alpine' + headers: + Sensu-Blivet: bar + annotations: + sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sensio.io.bonsai.tier: Community + sensio.io.bonsai.version: 4.0.0 + sensio.io.bonsai.tags: ruby-runtime-2.4.4 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'asset' + - result.object.builds | length == 2 + - result.object.metadata.annotations | dict2items | length == 4 + + - name: Test asset creation idempotence + asset: + auth: + url: http://localhost:8080 + name: asset + builds: + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_alpine_linux_amd64.tar.gz + sha512: b2da25ecd7642e6de41fde37d674fe19dcb6ee3d680e145e32289f7cfc352e6b5f9413ee9b701d61faeaa47b399aa30b25885dbc1ca432c4061c8823774c28f3 + filters: + - entity.system.platform == 'alpine' + - entity.system.os == 'linux' + - entity.system.arch == 'amd64' + headers: + Sensu-Blivet: bar + - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b + filters: + - entity.system.arch == 'amd64' + - entity.system.platform == 'rhel' + - entity.system.os == 'linux' + headers: + Sensu-Blivet: foo + annotations: + sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sensio.io.bonsai.tier: Community + sensio.io.bonsai.version: 4.0.0 + sensio.io.bonsai.tags: ruby-runtime-2.4.4 + register: result + + - assert: + that: result is not changed + + - name: Modify an asset + asset: + auth: + url: http://localhost:8080 + name: asset + builds: + - url: https://assets.bonsai.sensu.io/73a6f8b6f56672630d83ec21676f9a6251094475/sensu-plugins-disk-checks_5.0.0_centos_linux_amd64.tar.gz + sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf + annotations: + sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz + sensio.io.bonsai.tier: Community + sensio.io.bonsai.version: 4.0.0 + register: result + + - assert: + that: + - result is changed + - result.object.builds | length == 1 + - result.object.builds.0.sha512 == '0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf' + - not result.object.builds.0.headers + - not result.object.builds.0.filters + - result.object.metadata.annotations | dict2items | length == 3 + - "'sensu.io.bonsai.tags' not in result.object.metadata.annotations" + + - name: Fetch a specific asset + asset_info: + auth: + url: http://localhost:8080 + name: asset + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'asset' + + - name: Fetch all assets + asset_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'asset' + + - name: Delete an asset + asset: + auth: + url: http://localhost:8080 + name: asset2 + state: absent + + - name: Fetch all assets again after deletion + asset_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'asset' + + - name: Try to fetch non-existing asset + asset_info: + auth: + url: http://localhost:8080 + name: bad-bad-asset + register: result + + - assert: + that: + - result.objects == [] + + - name: Create an asset with a deprecated definition + shell: + cmd: | + cat <<EOF | sensuctl create + type: Asset + api_version: core/v2 + metadata: + name: old_asset + namespace: default + spec: + url: https://example.com/sensu-cpu-check_0.0.3_linux_amd64.tar.gz + sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf + filters: + - entity.system.os == 'linux' + - entity.system.arch == 'amd64' + EOF + + - name: Update deprecated asset + asset: + auth: + url: http://localhost:8080 + name: old_asset + builds: + - url: https://example.com/sensu-cpu-check_0.0.3_linux_amd64.tar.gz + sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf + filters: + - entity.system.os == 'linux' + - entity.system.arch == 'amd64' + register: result + + - assert: + that: result is changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml new file mode 100644 index 000000000..645ebe644 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml @@ -0,0 +1,230 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a check with missing required parameters + check: + auth: + url: http://localhost:8080 + name: minimal_check + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: subscriptions, command'" + + + - name: Create a check with minimal parameters + check: + auth: + url: http://localhost:8080 + name: minimal_check + command: /bin/true + subscriptions: + - checks + - also_checks + interval: 30 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_check' + + - name: Create a check with minimal parameters idempotence + check: + auth: + url: http://localhost:8080 + name: minimal_check + command: /bin/true + subscriptions: + - checks + - also_checks + interval: 30 + register: result + + - assert: + that: result is not changed + + - name: Create a check + check: &check + auth: + url: http://localhost:8080 + name: check + command: /bin/true + subscriptions: + - checks + - also_checks + handlers: + - default + - not_default + interval: 30 + publish: True + timeout: 30 + ttl: 100 + stdin: False + low_flap_threshold: 20 + high_flap_threshold: 60 + proxy_entity_name: switch-dc-01 + proxy_requests: + entity_attributes: ['entity.entity_class == "proxy"'] + splay: True + splay_coverage: 90 + output_metric_format: nagios_perfdata + output_metric_handlers: ['influx-db'] + round_robin: True + env_vars: + foo: bar + runtime_assets: awesomeness + secrets: + - name: test + secret: value + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'check' + - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]" + + - name: Test check creation idempotence + check: *check + register: result + + - assert: + that: result is not changed + + - name: Modify a check + check: + auth: + url: http://localhost:8080 + name: check + interval: 30 + command: /bin/true + subscriptions: + - checks + register: result + + - assert: + that: + - result is changed + - not result.object.handlers + - not result.object.env_vars + - not result.object.runtime_assets + - "'also_checks' not in result.object.subscriptions" + + - name: Create a second check + check: + auth: + url: http://localhost:8080 + name: check2 + interval: 30 + command: /usr/bin/true + subscriptions: checks + handlers: default + + - name: Fetch all checks + check_info: + auth: + url: http://localhost:8080 + + - name: Fetch a specific check + check_info: + auth: + url: http://localhost:8080 + name: check + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'check' + + - name: Delete a check + check: + auth: + url: http://localhost:8080 + name: check + state: absent + + - name: Get all checks + check_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'check2' + + - name: Try to fetch non-existing check + check_info: + auth: + url: http://localhost:8080 + name: bad-bad-check + register: result + + - assert: + that: + - result.objects == [] + + - name: Create check for idempotency test of complex fields + check: + name: complex + command: sleep 10 + interval: 10 + subscriptions: + - sub1 + - sub2 + handlers: [] + proxy_requests: + entity_attributes: + - "entity.entity_class == 'proxy'" + - "entity.entity_class == 'demo'" + check_hooks: + warning: + - h1 + - h2 + - h3 + error: + - h4 + - h2 + env_vars: + var1: val1 + var2: val2 + + - name: Test for idempotency test of complex fields + check: + name: complex + command: sleep 10 + interval: 10 + subscriptions: + - sub2 + - sub1 + runtime_assets: [] + proxy_requests: + entity_attributes: + - "entity.entity_class == 'demo'" + - "entity.entity_class == 'proxy'" + check_hooks: + warning: + - h1 + - h3 + - h2 + error: + - h2 + - h4 + env_vars: + var2: val2 + var1: val1 + register: result + + - assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml new file mode 100644 index 000000000..d92d56f85 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml @@ -0,0 +1,171 @@ +--- +- name: Converge + hosts: all + gather_facts: false + environment: + SENSU_ANSIBLE_DEBUG: "true" + + tasks: + - name: Make sure we start clean + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result is success + - result.objects | length == 0 + + - name: Create new cluster (check mode) + sensu.sensu_go.cluster: &cluster + name: alpha-cluster + api_urls: http://10.10.0.1:8080 + check_mode: true + register: result + - ansible.builtin.assert: &cluster-assertions + that: + - result is changed + - result.object.metadata.name == "alpha-cluster" + - result.object.api_urls == ["http://10.10.0.1:8080"] + + - name: Make sure things are still clean + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create new cluster + sensu.sensu_go.cluster: *cluster + register: result + - ansible.builtin.debug: + var: result + - ansible.builtin.assert: *cluster-assertions + + - name: Make sure cluster is present on the backend + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "alpha-cluster" + - result.objects.0.api_urls == ["http://10.10.0.1:8080"] + + - name: Create new cluster (idempotence) + sensu.sensu_go.cluster: *cluster + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update existing cluster (check mode) + sensu.sensu_go.cluster: &update + name: alpha-cluster + api_urls: + - http://10.10.0.2:8080 + check_mode: true + register: result + - ansible.builtin.assert: &update-assertions + that: + - result is changed + - result.object.metadata.name == "alpha-cluster" + - result.object.api_urls == ["http://10.10.0.2:8080"] + + - name: Make sure cluster did not change + sensu.sensu_go.cluster_info: + name: alpha-cluster + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.api_urls == ["http://10.10.0.1:8080"] + + - name: Update existing cluster + sensu.sensu_go.cluster: *update + register: result + - ansible.builtin.assert: *update-assertions + + - name: Make sure cluster is updated + sensu.sensu_go.cluster_info: + name: alpha-cluster + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "alpha-cluster" + - result.objects.0.api_urls == ["http://10.10.0.2:8080"] + + - name: Update existing cluster (idempotence) + sensu.sensu_go.cluster: *update + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create additional cluster + sensu.sensu_go.cluster: + name: beta-cluster + api_urls: + - https://10.20.0.1:8080 + - https://10.20.0.2:8080 + register: result + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == "beta-cluster" + - result.object.api_urls == [ + "https://10.20.0.1:8080", + "https://10.20.0.2:8080", + ] + + - name: Make sure we have two replicators now + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete cluster (check mode) + sensu.sensu_go.cluster: &delete + name: alpha-cluster + state: absent + check_mode: true + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure we still have two replicators + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete cluster + sensu.sensu_go.cluster: *delete + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Now we have only one cluster + sensu.sensu_go.cluster_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "beta-cluster" + + - name: And the first cluster is no more + sensu.sensu_go.cluster_info: + name: alpha-cluster + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Delete cluster (idempotency) + sensu.sensu_go.cluster: *delete + register: result + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml new file mode 100644 index 000000000..b771c76d3 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml @@ -0,0 +1,219 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a cluster role with missing required parameters + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: rules'" + + - name: Create a cluster role with empty rules + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + rules: [] + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: rules'" + + - name: Create a cluster role with invalid rule verbs + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + rules: + - verbs: + - list + - do_something + resources: + - entities + ignore_errors: true + register: result + + - assert: + that: + - result is failed + + - name: Create a cluster role with minimal parameters + cluster_role: + auth: + url: http://localhost:8080 + name: minimal_test_cluster_role + rules: + - verbs: + - get + - list + resources: + - entities + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_cluster_role' + - result.object.rules | length == 1 + - result.object.rules.0.verbs | length == 2 + - result.object.rules.0.verbs == ['get', 'list'] + - result.object.rules.0.resources == ['entities'] + + - name: Check idempotence of cluster role creation with minimal parameters + cluster_role: + auth: + url: http://localhost:8080 + name: minimal_test_cluster_role + rules: + - verbs: + - list + - get + resources: + - entities + register: result + + - assert: + that: result is not changed + + - name: Create a cluster role + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + rules: + - verbs: + - list + resources: + - assets + - checks + resource_names: + - some_resource_1 + - some_resource_2 + - verbs: + - list + - get + resources: + - checks + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_cluster_role' + - result.object.rules | length == 2 + - result.object.rules.0.verbs | length == 1 + - result.object.rules.0.verbs == ['list'] + - result.object.rules.0.resources == ['assets', 'checks'] + - result.object.rules.0.resource_names == ['some_resource_1', 'some_resource_2'] + - result.object.rules.1.verbs == ['list', 'get'] + - result.object.rules.1.resources == ['checks'] + - not result.object.rules.1.resource_names + + - name: Check idempotence of cluster role creation + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + rules: + - verbs: + - list + resources: + - checks + - assets + resource_names: + - some_resource_2 + - some_resource_1 + - verbs: + - get + - list + resources: + - checks + register: result + + - assert: + that: + - result is not changed + + - name: Modify a cluster role + cluster_role: + auth: + url: http://localhost:8080 + name: test_cluster_role + rules: + - verbs: + - list + resources: + - assets + register: result + + - assert: + that: + - result is changed + - result.object.rules | length == 1 + - result.object.rules.0.verbs == ['list'] + - result.object.rules.0.resources == ['assets'] + - not result.object.rules.0.resource_names + + - name: Fetch all cluster roles + cluster_role_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 8 # There are 6 default cluster roles + + - name: Fetch a specific cluster role + cluster_role_info: + auth: + url: http://localhost:8080 + name: test_cluster_role + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'test_cluster_role' + + - name: Delete a cluster role + cluster_role: + auth: + url: http://localhost:8080 + state: absent + name: minimal_test_cluster_role + register: result + + - name: Fetch all cluster roles after deletion of a cluster role + cluster_role_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 7 # There are 6 default cluster roles + + - name: Try to fetch non-existing role + cluster_role_info: + auth: + url: http://localhost:8080 + name: bad-bad-role + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml new file mode 100644 index 000000000..5fbb40def --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml @@ -0,0 +1,194 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a cluster role binding with missing required parameters + cluster_role_binding: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: cluster_role'" + + - name: Create a cluster role binding without providing users or groups + cluster_role_binding: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + cluster_role: test_cluster_role + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'missing required parameters: users or groups'" + + - name: Create a cluster role binding with minimal parameters (users only) + cluster_role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_cluster_role_binding_users + cluster_role: test_cluster_role + users: + - test_user + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_cluster_role_binding_users' + - result.object.role_ref.name == 'test_cluster_role' + - result.object.subjects | length == 1 + - result.object.subjects.0.name == 'test_user' + - result.object.subjects.0.type == 'User' + + - name: Create a cluster role binding with minimal parameters (groups only) + cluster_role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_cluster_role_binding_groups + cluster_role: test_cluster_role + groups: + - test_group_1 + - test_group_2 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_cluster_role_binding_groups' + - result.object.role_ref.name == 'test_cluster_role' + - result.object.subjects | length == 2 + + - name: Check idempotence of cluster role binding creation with minimal parameters + cluster_role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_cluster_role_binding_groups + cluster_role: test_cluster_role + groups: + - test_group_2 + - test_group_1 + register: result + + - assert: + that: result is not changed + + - name: Create a cluster role binding + cluster_role_binding: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + cluster_role: test_cluster_role + users: + - test_user_1 + - test_user_2 + groups: + - test_group_1 + - test_group_2 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_cluster_role_binding' + - result.object.role_ref.name == 'test_cluster_role' + - result.object.subjects | length == 4 + + - name: Check idempotence of cluster role binding creation + cluster_role_binding: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + cluster_role: test_cluster_role + users: + - test_user_2 + - test_user_1 + groups: + - test_group_2 + - test_group_1 + register: result + + - assert: + that: + - result is not changed + + - name: Modify a cluster role binding + cluster_role_binding: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + cluster_role: another_cluster_role + users: + groups: + - group_1 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_cluster_role_binding' + - result.object.role_ref.name == 'another_cluster_role' + - result.object.subjects | length == 1 + - result.object.subjects.0.name == 'group_1' + - result.object.subjects.0.type == 'Group' + + - name: Fetch all cluster role bindings + cluster_role_binding_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 6 # There are 3 pre-existing cluster role bindings by default + + - name: Fetch a specific cluster role binding + cluster_role_binding_info: + auth: + url: http://localhost:8080 + name: test_cluster_role_binding + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'test_cluster_role_binding' + + - name: Delete a cluster role binding + cluster_role_binding: + auth: + url: http://localhost:8080 + state: absent + name: test_cluster_role_binding + register: result + + - name: Fetch all cluster roles bindings after deletion + cluster_role_binding_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 5 # There are 3 pre-existing cluster role bindings by default + + - name: Try to fetch non-existing binding + cluster_role_binding_info: + auth: + url: http://localhost:8080 + name: bad-bad-binding + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml new file mode 100644 index 000000000..a6963f973 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml @@ -0,0 +1,133 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Retrieve empty list of external datastores + datastore_info: + auth: &auth + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects == [] + + - name: Make sure we fail creation if dsn parameter is missing + datastore: + auth: *auth + name: my-incomplete-datastore + register: result + ignore_errors: true + + - assert: + that: + - result is failed + + - name: Enable external datastore with minimal parameters + datastore: &idempotence + auth: *auth + name: my-datastore + dsn: postgresql://user:secret@host:port/dbname + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == "my-datastore" + - result.object.dsn == "postgresql://user:secret@host:port/dbname" + + - name: Check for idempotence + datastore: *idempotence + register: result + + - assert: + that: + - result is not changed + + - name: Try to add another external storage + datastore: + auth: *auth + name: my-second-datastore + dsn: postgresql://user:secret@host:port/db + pool_size: 123 + register: result + ignore_errors: true + + - assert: + that: + - result is failed + + - name: Update external datastore + datastore: + auth: *auth + name: my-datastore + dsn: postgresql://user:secret@host:port/new + pool_size: 321 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == "my-datastore" + - result.object.dsn == "postgresql://user:secret@host:port/new" + - result.object.pool_size == 321 + + - name: Fetch all datastores + datastore_info: + auth: *auth + register: result + + - assert: + that: + - result.objects | length == 1 + + - name: Fetch a specific datastore + datastore_info: + auth: *auth + name: my-datastore + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "my-datastore" + - result.objects.0.dsn == "postgresql://user:secret@host:port/new" + - result.objects.0.pool_size == 321 + + - name: Remove external datastore + datastore: + auth: *auth + name: my-datastore + state: absent + + - name: Re-fetch all datastores + datastore_info: + auth: *auth + register: result + + - assert: + that: + - result.objects | length == 0 + + - name: Try to fetch non-existing datastore + datastore_info: + auth: *auth + name: my-fictional-datastore + register: result + + - assert: + that: + - result.objects == [] + + - name: Try to remove non-existing external datastore + datastore: + auth: *auth + name: my-fictional-datastore + state: absent + + - assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml new file mode 100644 index 000000000..4cca68495 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml @@ -0,0 +1,191 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create entity with minimal parameters + entity: + auth: + url: http://localhost:8080 + name: minimal_entity + entity_class: proxy + register: result + + - assert: + that: + - result is changed + - result.object.entity_class == 'proxy' + - result.object.metadata.name == 'minimal_entity' + + - name: Create entity with minimal parameters idempotence + entity: + auth: + url: http://localhost:8080 + name: minimal_entity + entity_class: proxy + register: result + + - assert: + that: + - result is not changed + + - name: Create entity + entity: + auth: + url: http://localhost:8080 + name: entity + entity_class: proxy + subscriptions: + - web + - prod + system: + hostname: playbook-entity + os: linux + platform: ubutntu + network: + interfaces: + - name: lo + addresses: + - 127.0.0.1/8 + - ::1/128 + - name: eth0 + mac: 52:54:00:20:1b:3c + addresses: + - 93.184.216.34/24 + last_seen: 1522798317 + deregister: yes + deregistration_handler: email-handler + redact: + - password + - pass + - api_key + user: agent + register: result + + - assert: + that: + - result is changed + - result.object.entity_class == 'proxy' + - result.object.subscriptions == ['web', 'prod'] + - result.object.system.network.interfaces.0.name == 'lo' + - result.object.system.network.interfaces.1.mac == '52:54:00:20:1b:3c' + - result.object.last_seen == 1522798317 + - result.object.deregister == True + - result.object.deregistration.handler == 'email-handler' + - result.object.redact == ['password', 'pass', 'api_key'] + - result.object.user == 'agent' + - result.object.metadata.name == 'entity' + + - name: Test entity creation idempotence + entity: + auth: + url: http://localhost:8080 + name: entity + entity_class: proxy + subscriptions: + - web + - prod + system: + hostname: playbook-entity + os: linux + platform: ubutntu + network: + interfaces: + - name: lo + addresses: + - 127.0.0.1/8 + - ::1/128 + - name: eth0 + mac: 52:54:00:20:1b:3c + addresses: + - 93.184.216.34/24 + last_seen: 1522798317 + deregister: yes + deregistration_handler: email-handler + redact: + - password + - pass + - api_key + user: agent + register: result + + - assert: + that: result is not changed + + - name: Modify entity + entity: + auth: + url: http://localhost:8080 + name: entity + entity_class: some_class + register: result + + - assert: + that: + - result is changed + - result.object.entity_class == 'some_class' + - "'deprecations' in result" + + - name: Create an agent entity + entity: &agent_entity + auth: + url: http://localhost:8080 + name: entity2 + entity_class: agent + + - name: Create an agent entity (idempotence) + entity: *agent_entity + register: result + + - assert: + that: + - result is not changed + + - name: Fetch all entities + entity_info: + auth: + url: http://localhost:8080 + + - name: Fetch a specific entity + entity_info: + auth: + url: http://localhost:8080 + name: entity + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'entity' + - result.objects.0.entity_class == 'some_class' + + - name: Delete entity + entity: + auth: + url: http://localhost:8080 + name: entity + state: absent + + - name: Fetch all entities + entity_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'entity2' + + - name: Try to fetch non-existing entity + entity_info: + auth: + url: http://localhost:8080 + name: bad-bad-entity + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml new file mode 100644 index 000000000..4b87df9e2 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml @@ -0,0 +1,181 @@ +--- +- name: Converge + hosts: all + gather_facts: false + + tasks: + - name: Make sure we start clean + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result is success + - result.objects | length == 0 + + - name: Create new replicator (check mode) + sensu.sensu_go.etcd_replicator: &replicator + name: cluster-role-replicator + ca_cert: /etc/sensu/certs/ca.pem + cert: /etc/sensu/certs/cert.pem + key: /etc/sensu/certs/key.pem + url: https://sensu.alpha.example.com:2379 + resource: ClusterRole + check_mode: true + register: result + - ansible.builtin.assert: &replicator-assertions + that: + - result is changed + - result.object.metadata.name == "cluster-role-replicator" + - result.object.resource == "ClusterRole" + - result.object.insecure is false + - result.object.cert == "/etc/sensu/certs/cert.pem" + + - name: Make sure things are still clean + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create new replicator + sensu.sensu_go.etcd_replicator: *replicator + register: result + - ansible.builtin.debug: + var: result + - ansible.builtin.assert: *replicator-assertions + + - name: Make sure replicator is present on the backend + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "cluster-role-replicator" + - result.objects.0.resource == "ClusterRole" + - result.objects.0.insecure is false + - result.objects.0.cert == "/etc/sensu/certs/cert.pem" + + - name: Create new replicator (idempotence) + sensu.sensu_go.etcd_replicator: *replicator + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update existing replicator (check mode) + sensu.sensu_go.etcd_replicator: &update + name: cluster-role-replicator + insecure: true + url: + - http://sensu.alpha.example.com:2379 + resource: ClusterRole + check_mode: true + register: result + - ansible.builtin.assert: &update-assertions + that: + - result is changed + - result.object.metadata.name == "cluster-role-replicator" + - result.object.resource == "ClusterRole" + - result.object.insecure is true + + - name: Make sure replicator did not change + sensu.sensu_go.etcd_replicator_info: + name: cluster-role-replicator + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.insecure is false + + - name: Update existing replicator + sensu.sensu_go.etcd_replicator: *update + register: result + - ansible.builtin.assert: *update-assertions + + - name: Make sure replicator is updated + sensu.sensu_go.etcd_replicator_info: + name: cluster-role-replicator + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "cluster-role-replicator" + - result.objects.0.resource == "ClusterRole" + - result.objects.0.insecure is true + + - name: Update existing replicator (idempotence) + sensu.sensu_go.etcd_replicator: *update + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create additional replicator + sensu.sensu_go.etcd_replicator: + name: new-replicator + insecure: true + url: http://dummy.url + resource: Role + namespace: default + register: result + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == "new-replicator" + - result.object.insecure is true + - result.object.resource == "Role" + - result.object.namespace == "default" + + - name: Make sure we have two replicators now + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete replicator (check mode) + sensu.sensu_go.etcd_replicator: &delete + name: cluster-role-replicator + state: absent + check_mode: true + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure we still have two replicators + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete replicator + sensu.sensu_go.etcd_replicator: *delete + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Now we have only one replicator + sensu.sensu_go.etcd_replicator_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == "new-replicator" + + - name: And the cluster role replicator is no more + sensu.sensu_go.etcd_replicator_info: + name: cluster-role-replicator + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Delete replicator (idempotency) + sensu.sensu_go.etcd_replicator: *delete + register: result + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml new file mode 100644 index 000000000..885698e1e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml @@ -0,0 +1,217 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Event info without entity + event_info: + auth: + url: http://localhost:8080 + check: simple-check + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == \"missing parameter(s) required by 'check': entity\"" + + - name: Create event with missing objects on remote + event: + auth: + url: http://localhost:8080 + timestamp: 134532453 + entity: entity + check: check + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - result.msg == "Entity with name 'entity' does not exist on remote." + + - name: Create simple entity + entity: + auth: + url: http://localhost:8080 + name: awesome_entity + entity_class: proxy + + - name: Create event with missing check on remote + event: + auth: + url: http://localhost:8080 + timestamp: 134532453 + entity: awesome_entity + check: check + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - result.msg == "Check with name 'check' does not exist on remote." + + - name: Create a simple check + check: + auth: + url: http://localhost:8080 + name: awesome_check + command: /bin/true + subscriptions: + - checks + - also_checks + interval: 30 + + - name: Get non-existing last event for entity and check combo + event_info: + auth: + url: http://localhost:8080 + entity: awesome_entity + check: awesome_check + register: result + + - assert: + that: + - result.objects == [] + + - name: Create event with minimal parameters + event: + auth: + url: http://localhost:8080 + entity: awesome_entity + check: awesome_check + register: result + + - assert: + that: + - result is changed + - result.object.check.metadata.name == 'awesome_check' + - result.object.check.command == '/bin/true' + - result.object.entity.metadata.name == 'awesome_entity' + + - name: Get last event + event_info: + auth: + url: http://localhost:8080 + entity: awesome_entity + check: awesome_check + register: result + + - assert: + that: + - result.objects.0.check.metadata.name == 'awesome_check' + - result.objects.0.check.command == '/bin/true' + - result.objects.0.entity.metadata.name == 'awesome_entity' + + - name: Get all events + event_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 1 + + - name: Create second entity + entity: + auth: + url: http://localhost:8080 + name: entity2 + entity_class: proxy + + - name: Create a second check + check: + auth: + url: http://localhost:8080 + name: check2 + command: /bin/false + subscriptions: + - checks + - also_checks + interval: 30 + + - name: Create event with all parameters + event: + auth: + url: http://localhost:8080 + entity: entity2 + check: check2 + check_attributes: + duration: 1.945 + executed: 1522100915 + history: + - executed: 1552505193 + status: 1 + - executed: 1552505293 + status: 0 + - executed: 1552505393 + status: 0 + - executed: 1552505493 + status: 0 + issued: 1552506034 + last_ok: 1552506033 + output: '10' + state: 'passing' + status: 'ok' + total_state_change: 0 + metric_attributes: + handlers: + - handler1 + - handler2 + points: + - name: "sensu-go-sandbox.curl_timings.time_total" + tags: # Both value of the pairs must be strings + - name: "response_time_in_ms" + value: "101" + timestamp: 1552506033 + value: 0.005 + - name: "sensu-go-sandbox.curl_timings.time_namelookup" + tags: + - name: "namelookup_time_in_ms" + value: "57" + timestamp: 1552506033 + value: 0.004 + register: result + + - assert: + that: + - result is changed + - result.object.entity.metadata.name == 'entity2' + - result.object.check.metadata.name == 'check2' + - result.object.check.command == '/bin/false' + - result.object.check.duration == 1.945 + - result.object.check.executed == 1522100915 + - "result.object.check.history.0 == {'executed': 1552505193, 'status': 1}" + - result.object.check.issued == 1552506034 + - result.object.check.last_ok == 1552506033 + - result.object.check.output == '10' + - result.object.check.state == 'passing' + - result.object.check.status == 0 + - result.object.check.total_state_change == 0 + + - name: Get events matching entity2 + event_info: + auth: + url: http://localhost:8080 + entity: entity2 + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.check.metadata.name == 'check2' + + - name: Get all events + event_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml new file mode 100644 index 000000000..6ae4f03a2 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml @@ -0,0 +1,150 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create filter with missing required parameters + filter: + auth: + url: http://localhost:8080 + name: filter + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: action, expressions'" + + - name: Create filter with minimal parameters + filter: + auth: + url: http://localhost:8080 + name: minimal_filter + action: allow + expressions: + - event.check.occurrences == 1 + register: result + + - assert: + that: + - result is changed + - result.object.action == 'allow' + - result.object.expressions == ['event.check.occurrences == 1'] + - result.object.metadata.name == 'minimal_filter' + + - name: Create filter with minimal parameters idempotence + filter: + auth: + url: http://localhost:8080 + name: minimal_filter + action: allow + expressions: + - event.check.occurences == 1 + register: result + + - name: Create a filter + filter: + auth: + url: http://localhost:8080 + name: filter + action: deny + expressions: + - event.check.interval == 10 + - event.check.occurrences == 1 + runtime_assets: awesomeness + register: result + + - assert: + that: + - result is changed + - result.object.action == 'deny' + - result.object.expressions == ['event.check.interval == 10', 'event.check.occurrences == 1'] + - result.object.runtime_assets == ['awesomeness'] + - result.object.metadata.name == 'filter' + + - name: Test filter creation idempotence + filter: + auth: + url: http://localhost:8080 + name: filter + action: deny + expressions: + - event.check.interval == 10 + - event.check.occurrences == 1 + runtime_assets: awesomeness + register: result + + - assert: + that: result is not changed + + - name: Create a second filter + filter: + auth: + url: http://localhost:8080 + name: filter2 + action: allow + expressions: event.check.interval == 10 + + - name: Fetch all filters + filter_info: + auth: + url: http://localhost:8080 + + - name: Fetch a specific filter + filter_info: + auth: + url: http://localhost:8080 + name: filter + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'filter' + + - name: Modify a filter + filter: + auth: + url: http://localhost:8080 + name: filter + action: deny + expressions: event.check.interval > 10 + register: result + + - assert: + that: + - result is changed + - result.object.expressions == ['event.check.interval > 10'] + - not result.object.runtime_assets + + - name: Delete a filter + filter: + auth: + url: http://localhost:8080 + name: filter + state: absent + + - name: Get all filters + filter_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'filter2' + + - name: Try to fetch non-existing filter + filter_info: + auth: + url: http://localhost:8080 + name: bad-bad-filter + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml new file mode 100644 index 000000000..5a71e7e9e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml @@ -0,0 +1,109 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create handler set + handler_set: + auth: + url: http://localhost:8080 + name: handler + handlers: + - udp_handler + - tcp_handler + register: result + + - assert: + that: + - result is changed + - result.object.handlers == ['udp_handler', 'tcp_handler'] + - result.object.metadata.name == 'handler' + + - name: Test handler set creation idempotence + handler_set: + auth: + url: http://localhost:8080 + name: handler + handlers: + - udp_handler + - tcp_handler + register: result + + - assert: + that: result is not changed + + - name: Modify handler set + handler_set: + auth: + url: http://localhost:8080 + name: handler + handlers: + - slack + register: result + + - assert: + that: + - result is changed + - result.object.handlers == ['slack'] + + - name: Fetch all set handlers + handler_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 1 + + - name: Fetch a specific handler set + handler_info: + auth: + url: http://localhost:8080 + name: handler + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'handler' + + - name: Delete handler set + handler_set: + auth: + url: http://localhost:8080 + name: handler + state: absent + + - name: Get the Sensu Go backend's version + uri: + url: http://localhost:8080/version + return_content: true + register: backend_version + + - name: Get all handlers set + handler_info: + auth: + url: http://localhost:8080 + register: result + # Sensu Go 5.15.0 returns none when no handler sets are present + when: (backend_version.content | from_json).sensu_backend != '5.15.0' + + - assert: + that: + - result.objects | length == 0 + # Sensu Go 5.15.0 returns none when no handler sets are present + when: (backend_version.content | from_json).sensu_backend != '5.15.0' + + - name: Try to fetch non-existing handler + handler_info: + auth: + url: http://localhost:8080 + name: bad-bad-handler + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml new file mode 100644 index 000000000..160c27d62 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml @@ -0,0 +1,139 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create hook with minimal parameters + hook: + auth: + url: http://localhost:8080 + name: minimal_hook + command: /bin/true + timeout: 10 + register: result + + - assert: + that: + - result is changed + - result.object.command == '/bin/true' + - result.object.metadata.name == 'minimal_hook' + + - name: Create hook with minimal parameters idempotence + hook: + auth: + url: http://localhost:8080 + name: minimal_hook + command: /bin/true + timeout: 10 + register: result + + - assert: + that: + - result is not changed + + - name: Create a hook + hook: + auth: + url: http://localhost:8080 + name: hook + command: /bin/true + timeout: 30 + stdin: yes + runtime_assets: + - ruby2.4 + register: result + + - assert: + that: + - result is changed + - result.object.command == '/bin/true' + - result.object.timeout == 30 + - result.object.stdin == True + - result.object.runtime_assets == ['ruby2.4'] + - result.object.metadata.name == 'hook' + + - name: Test hook creation idempotence + hook: + auth: + url: http://localhost:8080 + name: hook + command: /bin/true + timeout: 30 + stdin: yes + runtime_assets: + - ruby2.4 + register: result + + - assert: + that: result is not changed + + - name: Modify a hook + hook: + auth: + url: http://localhost:8080 + name: hook + command: python -c "print('Modified.')" + timeout: 60 + register: result + + - assert: + that: + - result is changed + - not result.object.stdin + - not result.object.runtime_assets + + - name: Create a second hook + hook: + auth: + url: http://localhost:8080 + name: hook2 + command: sensu-influxdb-hook + timeout: 30 + + - name: Fetch all hooks + hook_info: + auth: + url: http://localhost:8080 + + - name: Fetch a specific hook + hook_info: + auth: + url: http://localhost:8080 + name: hook + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'hook' + + - name: Delete a hook + hook: + auth: + url: http://localhost:8080 + name: hook + state: absent + + - name: Fetch all hooks + hook_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'hook2' + + - name: Try to fetch non-existing hook + hook_info: + auth: + url: http://localhost:8080 + name: bad-bad-hook + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml new file mode 100644 index 000000000..053bdf82d --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml @@ -0,0 +1,358 @@ +--- +- name: Converge + hosts: all + gather_facts: no + tasks: + - name: Fetch all LDAP auth providers and verify the presence + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Fail when trying to create a LDAP auth provider with missing required params + sensu.sensu_go.ldap_auth_provider: + auth: + url: http://localhost:8080 + name: openldap + register: result + ignore_errors: true + + - ansible.builtin.assert: + that: + - result is failed + - "'state is present but all of the following are missing' in result.msg" + + - name: Create LDAP auth provider with minimal params (check mode) + sensu.sensu_go.ldap_auth_provider: &create-provider + state: present + name: openldap + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + check_mode: true + register: result + + - ansible.builtin.assert: &create-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'openldap' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + + - name: Make sure LDAP auth provider was not created when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create LDAP auth provider with minimal params + sensu.sensu_go.ldap_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: *create-provider-assertions + + - name: Make sure LDAP auth provider was created + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'openldap' + + - name: Idempotence check for LDAP auth provider creation with minimal params + sensu.sensu_go.ldap_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update LDAP auth provider (check mode) + sensu.sensu_go.ldap_auth_provider: &update-provider + state: present + name: openldap + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + groups_prefix: dev + check_mode: true + register: result + + - ansible.builtin.assert: &update-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'openldap' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.object.groups_prefix == 'dev' + + - name: Make sure LDAP auth provider was not updated in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'openldap' + - result.objects.0.servers.0.host == '127.0.0.1' + - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.groups_prefix == '' + + - name: Update LDAP auth provider + sensu.sensu_go.ldap_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: *update-provider-assertions + + - name: Make sure LDAP auth provider was updated + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'openldap' + - result.objects.0.servers.0.host == '127.0.0.1' + - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.objects.0.groups_prefix == 'dev' + + - name: Idempotence check for LDAP auth provider modification + sensu.sensu_go.ldap_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Add LDAP auth provider server to existing one (check mode) + sensu.sensu_go.ldap_auth_provider: &create-extra-provider + state: present + name: openldap + servers: + - host: 127.0.0.1 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + - host: 127.0.0.2 + group_search: + base_dn: dc=acme,dc=org + user_search: + base_dn: dc=acme,dc=org + groups_prefix: dev + check_mode: true + register: result + + - ansible.builtin.assert: &create-extra-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'openldap' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.1.host == '127.0.0.2' + - result.object.servers.1.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.1.user_search.base_dn == 'dc=acme,dc=org' + + - name: Make sure extra LDAP auth provider was not created when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.servers | length == 1 + + - name: Create an extra LDAP auth provider + sensu.sensu_go.ldap_auth_provider: *create-extra-provider + register: result + + - ansible.builtin.assert: *create-extra-provider-assertions + + - name: Make sure extra LDAP auth provider was created + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.servers | length == 2 + - result.objects.0.metadata.name == 'openldap' + + - name: Idempotence check for extra LDAP auth provider creation + sensu.sensu_go.ldap_auth_provider: *create-extra-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create a LDAP auth provider with all params + sensu.sensu_go.ldap_auth_provider: &create-provider-all-params + auth: + url: http://localhost:8080 + state: present + name: other-openldap + servers: + - host: 127.0.0.1 + port: 636 + insecure: false + security: tls + trusted_ca_file: /path/to/trusted-certificate-authorities.pem + client_cert_file: /path/to/ssl/cert.pem + client_key_file: /path/to/ssl/key.pem + binding: + user_dn: cn=binder,dc=acme,dc=org + password: ldap_password + group_search: + base_dn: dc=acme,dc=org + attribute: member + name_attribute: cn + object_class: groupOfNames + user_search: + base_dn: dc=acme,dc=org + attribute: uid + name_attribute: cn + object_class: person + groups_prefix: dev + username_prefix: ldap + register: result + + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == 'other-openldap' + - result.object.servers.0.host == '127.0.0.1' + - result.object.servers.0.port == 636 + - result.object.servers.0.insecure == false + - result.object.servers.0.security == 'tls' + - result.object.servers.0.trusted_ca_file == '/path/to/trusted-certificate-authorities.pem' + - result.object.servers.0.client_cert_file == '/path/to/ssl/cert.pem' + - result.object.servers.0.client_key_file == '/path/to/ssl/key.pem' + - result.object.servers.0.binding.user_dn == 'cn=binder,dc=acme,dc=org' + - "'password' not in result.object.servers.0.binding" + - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.group_search.attribute == 'member' + - result.object.servers.0.group_search.name_attribute == 'cn' + - result.object.servers.0.group_search.object_class == 'groupOfNames' + - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org' + - result.object.servers.0.user_search.attribute == 'uid' + - result.object.servers.0.user_search.name_attribute == 'cn' + - result.object.servers.0.user_search.object_class == 'person' + - result.object.groups_prefix == 'dev' + - result.object.username_prefix == 'ldap' + + - name: Idempotence check for LDAP auth provider creation with all params + sensu.sensu_go.ldap_auth_provider: *create-provider-all-params + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Delete LDAP auth provider (check mode) + sensu.sensu_go.ldap_auth_provider: &delete-provider + auth: + url: http://localhost:8080 + name: openldap + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure LDAP auth provider was not deleted when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete LDAP auth provider + sensu.sensu_go.ldap_auth_provider: *delete-provider + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure LDAP auth provider was deleted + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: openldap + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Check if still any LDAP auth providers exist + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete LDAP auth provider + sensu.sensu_go.ldap_auth_provider: + auth: + url: http://localhost:8080 + name: other-openldap + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Delete a non-existent LDAP auth provider + sensu.sensu_go.ldap_auth_provider: + auth: + url: http://localhost:8080 + name: i-dont-exist + state: absent + register: result + + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml new file mode 100644 index 000000000..52db18f85 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml @@ -0,0 +1,144 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create mutator with minimal parameters + mutator: + auth: + url: http://localhost:8080 + name: minimal_mutator + command: /bin/true + register: result + + - assert: + that: + - result is changed + - result.object.command == '/bin/true' + - result.object.metadata.name == 'minimal_mutator' + + - name: Create mutator with minimal parameters idempotence + mutator: + auth: + url: http://localhost:8080 + name: minimal_mutator + command: /bin/true + register: result + + - assert: + that: + - result is not changed + + - name: Create a mutator + mutator: + auth: + url: http://localhost:8080 + name: mutator + command: sensu-influxdb-mutator + timeout: 30 + env_vars: + INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086 + INFLUXDB_USER: sensu + runtime_assets: + - sensu-influxdb-mutator + register: result + + - assert: + that: + - result is changed + - result.object.command == 'sensu-influxdb-mutator' + - result.object.timeout == 30 + - result.object.runtime_assets == ['sensu-influxdb-mutator'] + - result.object.metadata.name == 'mutator' + + - name: Test mutator creation idempotence + mutator: + auth: + url: http://localhost:8080 + name: mutator + command: sensu-influxdb-mutator + timeout: 30 + env_vars: + INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086 + INFLUXDB_USER: sensu + runtime_assets: + - sensu-influxdb-mutator + register: result + + - assert: + that: result is not changed + + - name: Modify a mutator + mutator: + auth: + url: http://localhost:8080 + name: mutator + command: sensu-influxdb-mutator + timeout: 60 + secrets: + - name: test + secret: value + register: result + + - assert: + that: + - result is changed + - not result.object.env_vars + - not result.object.runtime_assets + - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]" + + - name: Create a second mutator + mutator: + auth: + url: http://localhost:8080 + name: mutator2 + command: sensu-influxdb-mutator + timeout: 30 + + - name: Fetch all mutators + mutator_info: + auth: + url: http://localhost:8080 + + - name: Fetch a specific mutator + mutator_info: + auth: + url: http://localhost:8080 + name: mutator + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'mutator' + + - name: Delete a mutator + mutator: + auth: + url: http://localhost:8080 + name: mutator + state: absent + + - name: Fetch all mutators + mutator_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + - result.objects.0.metadata.name == 'minimal_mutator' + + - name: Try to fetch non-existing mutator + mutator_info: + auth: + url: http://localhost:8080 + name: bad-bad-mutator + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml new file mode 100644 index 000000000..e7f332e2f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml @@ -0,0 +1,78 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Fetch all namespaces + namespace_info: + auth: + url: http://localhost:8080 + register: default_state + + - name: store starting length + set_fact: + starting_length: "{{ default_state.objects | length }}" + + - name: Create namespace + namespace: + auth: + url: http://localhost:8080 + name: dev + register: result + + - assert: + that: + - result is changed + - result.object.name == 'dev' + + - name: Create namespace idempotence + namespace: + auth: + url: http://localhost:8080 + name: dev + register: result + + - assert: + that: + - result is not changed + + - name: Create a second namespace + namespace: + auth: + url: http://localhost:8080 + name: production + + - name: Fetch all namespaces + namespace_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == expected_length | int + - result.objects.1.name == 'dev' + vars: + expected_length: "{{ starting_length|int + 2 }}" + + - name: Delete namespace + namespace: + auth: + url: http://localhost:8080 + name: dev + state: absent + + - name: Fetch all namespaces + namespace_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == expected_length | int + - result.objects.1.name == 'production' + vars: + expected_length: "{{ starting_length|int + 1 }}" diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml new file mode 100644 index 000000000..89f42f48a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml @@ -0,0 +1,276 @@ +--- +- name: Converge + hosts: all + gather_facts: no + tasks: + - name: Fetch all auth providers and verify the presence + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Fail when trying to create a OIDC auth provider with missing required params + sensu.sensu_go.oidc_auth_provider: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + ignore_errors: true + + - ansible.builtin.assert: + that: + - result is failed + - "'state is present but all of the following are missing' in result.msg" + + - name: Create OIDC auth provider with minimal params (check mode) + sensu.sensu_go.oidc_auth_provider: &create-provider + state: present + name: oidc_name + client_id: a8e43af034e7f2608780 + client_secret: b63968394be6ed2edb61c93847ee792f31bf6216 + server: https://oidc.example.com:9031 + username_claim: email + check_mode: true + register: result + + - ansible.builtin.assert: &create-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'oidc_name' + - result.object.client_id == 'a8e43af034e7f2608780' + - "'client_secret' not in result.object" + - result.object.server == 'https://oidc.example.com:9031' + - result.object.username_claim == 'email' + + - name: Make sure OIDC auth provider was not created when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create OIDC auth provider with minimal params + sensu.sensu_go.oidc_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: *create-provider-assertions + + - name: Make sure OIDC auth provider was created + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'oidc_name' + + - name: Idempotence check for OIDC auth provider creation with minimal params + sensu.sensu_go.oidc_auth_provider: *create-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update OIDC auth provider (check mode) + sensu.sensu_go.oidc_auth_provider: &update-provider + state: present + name: oidc_name + additional_scopes: + - groups + - email + - username + client_id: a8e43af034e7f2608780 + client_secret: b63968394be6ed2edb61c93847ee792f31bf6216 + server: https://oidc.example.com:9031 + username_claim: email + check_mode: true + register: result + + - ansible.builtin.assert: &update-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'oidc_name' + - result.object.additional_scopes == ['groups', 'email', 'username'] + - result.object.client_id == 'a8e43af034e7f2608780' + - "'client_secret' not in result.object" + - result.object.server == 'https://oidc.example.com:9031' + - result.object.username_claim == 'email' + + - name: Make sure OIDC auth provider was not updated in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'oidc_name' + - result.objects.0.additional_scopes == ['openid'] + - result.objects.0.client_id == 'a8e43af034e7f2608780' + - "'client_secret' not in result.objects.0" + - result.objects.0.server == 'https://oidc.example.com:9031' + - result.objects.0.username_claim == 'email' + + - name: Update OIDC auth provider + sensu.sensu_go.oidc_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: *update-provider-assertions + + - name: Make sure OIDC auth provider was updated + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'oidc_name' + - result.objects.0.additional_scopes == ['groups', 'email', 'username'] + - result.objects.0.client_id == 'a8e43af034e7f2608780' + - "'client_secret' not in result.objects.0" + - result.objects.0.server == 'https://oidc.example.com:9031' + - result.objects.0.username_claim == 'email' + + - name: Idempotence check for OIDC auth provider modification + sensu.sensu_go.oidc_auth_provider: *update-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create a OIDC auth provider with all params + sensu.sensu_go.oidc_auth_provider: &create-provider-all-params + auth: + url: http://localhost:8080 + state: present + name: other-oidc_name + additional_scopes: + - groups + - email + - username + client_id: a8e43af034e7f2608780 + client_secret: b63968394be6ed2edb61c93847ee792f31bf6216 + disable_offline_access: false + redirect_uri: http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback + server: https://oidc.example.com:9031 + groups_claim: groups + groups_prefix: 'oidc:' + username_claim: email + username_prefix: 'oidc:' + register: result + + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == 'other-oidc_name' + - result.object.additional_scopes == ['groups', 'email', 'username'] + - result.object.client_id == 'a8e43af034e7f2608780' + - "'client_secret' not in result.object" + - result.object.disable_offline_access == false + - result.object.redirect_uri == 'http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback' + - result.object.server == 'https://oidc.example.com:9031' + - result.object.groups_claim == 'groups' + - result.object.groups_prefix == 'oidc:' + - result.object.username_claim == 'email' + - result.object.username_prefix == 'oidc:' + + - name: Idempotence check for OIDC auth provider creation with all params + sensu.sensu_go.oidc_auth_provider: *create-provider-all-params + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Delete OIDC auth provider (check mode) + sensu.sensu_go.oidc_auth_provider: &delete-provider + auth: + url: http://localhost:8080 + name: oidc_name + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure OIDC auth provider was not deleted when running in check mode + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete OIDC auth provider + sensu.sensu_go.oidc_auth_provider: *delete-provider + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure OIDC auth provider was deleted + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + name: oidc_name + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Check if still any OIDC auth providers exist + sensu.sensu_go.auth_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete OIDC auth provider + sensu.sensu_go.oidc_auth_provider: + auth: + url: http://localhost:8080 + name: other-oidc_name + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Delete a non-existent OIDC auth provider + sensu.sensu_go.oidc_auth_provider: + auth: + url: http://localhost:8080 + name: i-dont-exist + state: absent + register: result + + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml new file mode 100644 index 000000000..8fe9f0682 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml @@ -0,0 +1,129 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create pipe handler with minimal parameters + pipe_handler: + auth: + url: http://localhost:8080 + name: minimal_handler + command: sensu-influxdb-handler + register: result + + - assert: + that: + - result is changed + - result.object.command == 'sensu-influxdb-handler' + - result.object.metadata.name == 'minimal_handler' + + - name: Create pipe handler with minimal parameters idempotence + pipe_handler: + auth: + url: http://localhost:8080 + name: minimal_handler + command: sensu-influxdb-handler + register: result + + - assert: + that: result is not changed + + - name: Create pipe handler + pipe_handler: &handler + auth: + url: http://localhost:8080 + name: handler + command: sensu-influxdb-handler + mutator: mutate_input + timeout: 30 + filters: + - occurances + - production + env_vars: + INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086 + INFLUXDB_USER: sensu + INFLUXDB_PASS: password + runtime_assets: + - sensu-influxdb-handler + secrets: + - name: test + secret: value + register: result + + - debug: + msg: "{{result}}" + + - assert: + that: + - result is changed + - result.object.command == 'sensu-influxdb-handler' + - result.object.mutator == 'mutate_input' + - result.object.timeout == 30 + - result.object.filters == ['occurances', 'production'] + - result.object.runtime_assets == ['sensu-influxdb-handler'] + - result.object.metadata.name == 'handler' + - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]" + + - name: Test pipe handler creation idempotence + pipe_handler: *handler + register: result + + - assert: + that: result is not changed + + - name: Modify pipe handler + pipe_handler: + auth: + url: http://localhost:8080 + name: handler + command: sensu-influxdb-handler + timeout: 60 + register: result + + - assert: + that: + - result is changed + - not result.object.env_vars + - not result.object.runtime_assets + + - name: Fetch all pipe handlers + handler_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + + - name: Fetch a specific pipe handler + handler_info: + auth: + url: http://localhost:8080 + name: handler + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'handler' + + - name: Delete pipe handler + pipe_handler: + auth: + url: http://localhost:8080 + name: handler + state: absent + + - name: Get all pipe handlers + handler_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'minimal_handler' diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml new file mode 100644 index 000000000..b1871e70e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml @@ -0,0 +1,220 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a role with missing required parameters + role: + auth: + url: http://localhost:8080 + name: test_role + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: rules'" + + - name: Create a role with empty rules + role: + auth: + url: http://localhost:8080 + name: test_role + rules: [] + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: rules'" + + - name: Create a role with invalid rule verbs + role: + auth: + url: http://localhost:8080 + name: test_role + rules: + - verbs: + - list + - do_something + resources: + - entities + ignore_errors: true + register: result + + - assert: + that: + - result is failed + + - name: Create a role with minimal parameters + role: + auth: + url: http://localhost:8080 + name: minimal_test_role + rules: + - verbs: + - get + - list + resources: + - entities + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_role' + - result.object.rules | length == 1 + - result.object.rules.0.verbs | length == 2 + - result.object.rules.0.verbs == ['get', 'list'] + - result.object.rules.0.resources == ['entities'] + + - name: Check idempotence of role creation with minimal parameters + role: + auth: + url: http://localhost:8080 + name: minimal_test_role + rules: + - verbs: + - list + - get + resources: + - entities + register: result + + - assert: + that: result is not changed + + - name: Create a role + role: + auth: + url: http://localhost:8080 + name: test_role + rules: + - verbs: + - list + resources: + - assets + - checks + resource_names: + - some_resource_1 + - some_resource_2 + - verbs: + - list + - get + resources: + - checks + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_role' + - result.object.rules | length == 2 + - result.object.rules.0.verbs | length == 1 + - result.object.rules.0.verbs == ['list'] + - result.object.rules.0.resources == ['assets', 'checks'] + - result.object.rules.0.resource_names == ['some_resource_1', 'some_resource_2'] + - result.object.rules.1.verbs == ['list', 'get'] + - result.object.rules.1.resources == ['checks'] + - not result.object.rules.1.resource_names + + - name: Check idempotence of role creation + role: + auth: + url: http://localhost:8080 + name: test_role + rules: + - verbs: + - list + resources: + - checks + - assets + resource_names: + - some_resource_2 + - some_resource_1 + - verbs: + - get + - list + resources: + - checks + register: result + + - assert: + that: + - result is not changed + + - name: Modify a role + role: + auth: + url: http://localhost:8080 + name: test_role + rules: + - verbs: + - list + resources: + - assets + register: result + + - assert: + that: + - result is changed + - result.object.rules | length == 1 + - result.object.rules.0.verbs == ['list'] + - result.object.rules.0.resources == ['assets'] + - not result.object.rules.0.resource_names + + - name: Fetch all roles + role_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + # We created two roles but there might be others present by default + - result.objects | length >= 2 + + - name: Fetch a specific role + role_info: + auth: + url: http://localhost:8080 + name: test_role + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'test_role' + + - name: Delete a role + role: + auth: + url: http://localhost:8080 + state: absent + name: minimal_test_role + register: result + + - name: Fetch all roles after deletion of a role + role_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length >= 1 + + - name: Try to fetch non-existing role + role_info: + auth: + url: http://localhost:8080 + name: bad-bad-role + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml new file mode 100644 index 000000000..25f796769 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml @@ -0,0 +1,230 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create a role binding with missing required parameters + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but any of the following are missing: role, cluster_role'" + + - name: Create a role binding with mutually exclusive role and cluster_role + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + role: test_role + cluster_role: test_cluster_role + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'parameters are mutually exclusive: role|cluster_role'" + + - name: Create a role binding without providing users or groups + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + role: test_role + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'missing required parameters: users or groups'" + + - name: Create a role binding with minimal parameters (users only) + role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_role_binding_users + role: test_role + users: + - test_user + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_role_binding_users' + - result.object.role_ref.name == 'test_role' + - result.object.subjects | length == 1 + - result.object.subjects.0.name == 'test_user' + - result.object.subjects.0.type == 'User' + + - name: Create a role binding with minimal parameters (groups only) + role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_role_binding_groups + role: test_role + groups: + - test_group_1 + - test_group_2 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'minimal_test_role_binding_groups' + - result.object.role_ref.name == 'test_role' + - result.object.subjects | length == 2 + + - name: Check idempotence of role binding creation with minimal parameters + role_binding: + auth: + url: http://localhost:8080 + name: minimal_test_role_binding_groups + role: test_role + groups: + - test_group_2 + - test_group_1 + register: result + + - assert: + that: result is not changed + + - name: Create a role binding + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + role: test_role + users: + - test_user_1 + - test_user_2 + groups: + - test_group_1 + - test_group_2 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_role_binding' + - result.object.role_ref.name == 'test_role' + - result.object.subjects | length == 4 + + - name: Check idempotence of role binding creation + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + role: test_role + users: + - test_user_2 + - test_user_1 + groups: + - test_group_2 + - test_group_1 + register: result + + - assert: + that: + - result is not changed + + - name: Modify a role binding + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding + cluster_role: test_cluster_role + users: + groups: + - group_1 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_role_binding' + - result.object.role_ref.name == 'test_cluster_role' + - result.object.role_ref.type == 'ClusterRole' + - result.object.subjects | length == 1 + - result.object.subjects.0.name == 'group_1' + - result.object.subjects.0.type == 'Group' + + - name: Create a role binding with cluster role + role_binding: + auth: + url: http://localhost:8080 + name: test_role_binding_with_cluster_role + cluster_role: test_cluster_role + users: + - user_1 + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'test_role_binding_with_cluster_role' + - result.object.role_ref.name == 'test_cluster_role' + - result.object.role_ref.type == 'ClusterRole' + - result.object.subjects | length == 1 + - result.object.subjects.0.name == 'user_1' + - result.object.subjects.0.type == 'User' + + - name: Fetch all role bindings + role_binding_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length >= 4 + + - name: Fetch a specific role binding + role_binding_info: + auth: + url: http://localhost:8080 + name: test_role_binding + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'test_role_binding' + + - name: Delete a role binding + role_binding: + auth: + url: http://localhost:8080 + state: absent + name: test_role_binding + register: result + + - name: Fetch all roles bindings after deletion + role_binding_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length >= 3 + + - name: Try to fetch non-existing binding + role_binding_info: + auth: + url: http://localhost:8080 + name: bad-bad-binding + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml new file mode 100644 index 000000000..cbb02fee5 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml @@ -0,0 +1,171 @@ +--- +- name: Converge + hosts: all + gather_facts: false + + tasks: + - name: Make sure we start clean + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result is success + - result.objects | length == 0 + + - name: Create new secret (check mode) + sensu.sensu_go.secret: &secret + name: sample-secret + provider: env + id: MY_ENV_VAR + check_mode: true + register: result + - ansible.builtin.assert: &secret-assertions + that: + - result is changed + - result.object.metadata.name == 'sample-secret' + - result.object.id == 'MY_ENV_VAR' + - result.object.provider == 'env' + + - name: Make sure things are still clean + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Create new secret + sensu.sensu_go.secret: *secret + register: result + - ansible.builtin.assert: *secret-assertions + + - name: Make sure secret is present on the backend + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'sample-secret' + - result.objects.0.id == 'MY_ENV_VAR' + - result.objects.0.provider == 'env' + + - name: Create new secret (idempotence) + sensu.sensu_go.secret: *secret + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update existing secret (check mode) + sensu.sensu_go.secret: &update + name: sample-secret + id: MY_NEW_ENV_VAR + provider: env + check_mode: true + register: result + - ansible.builtin.assert: &update-assertions + that: + - result is changed + - result.object.metadata.name == 'sample-secret' + - result.object.id == 'MY_NEW_ENV_VAR' + - result.object.provider == 'env' + + - name: Make sure secret did not change + sensu.sensu_go.secret_info: + name: sample-secret + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'sample-secret' + - result.objects.0.id == 'MY_ENV_VAR' + - result.objects.0.provider == 'env' + + - name: Update existing secret + sensu.sensu_go.secret: *update + register: result + - ansible.builtin.assert: *update-assertions + + - name: Make sure secret is updated + sensu.sensu_go.secret_info: + name: sample-secret + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'sample-secret' + - result.objects.0.id == 'MY_NEW_ENV_VAR' + - result.objects.0.provider == 'env' + + - name: Update existing secret (idempotence) + sensu.sensu_go.secret: *update + register: result + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create additional secret + sensu.sensu_go.secret: + name: additional-secret + provider: hashi-vault + id: secret/database#pass + register: result + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == 'additional-secret' + - result.object.id == 'secret/database#pass' + - result.object.provider == 'hashi-vault' + + - name: Make sure we have two secrets now + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete secret (check mode) + sensu.sensu_go.secret: &delete + name: sample-secret + state: absent + check_mode: true + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure we still have two secrets + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 2 + + - name: Delete secret + sensu.sensu_go.secret: *delete + register: result + - ansible.builtin.assert: + that: + - result is changed + + - name: Now we have only one secret + sensu.sensu_go.secret_info: + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'additional-secret' + + - name: And the sample-secret is no more + sensu.sensu_go.secret_info: + name: sample-secret + register: result + - ansible.builtin.assert: + that: + - result.objects | length == 0 + + - name: Delete secret (idempotency) + sensu.sensu_go.secret: *delete + register: result + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml new file mode 100644 index 000000000..db34cca9b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml @@ -0,0 +1,127 @@ +--- +- name: Converge + hosts: all + gather_facts: no + tasks: + - name: Fetch all secrets providers and verify the presence of the + default env provider + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 # env provider is present by default + - result.objects.0.metadata.name == 'env' + + - name: Check idempotence of env provider creation + sensu.sensu_go.secrets_provider_env: &create-provider + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Delete the default env secrets provider (check mode) + sensu.sensu_go.secrets_provider_env: &delete-provider + auth: + url: http://localhost:8080 + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure we didn't delete the env secrets provider in check mode + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete the default env secrets provider + sensu.sensu_go.secrets_provider_env: *delete-provider + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure env secrets provider was deleted + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: env + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Delete a non-existent env secrets provider + sensu.sensu_go.secrets_provider_env: + auth: + url: http://localhost:8080 + state: absent + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Fetch a non-existent secrets provider + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: env + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Re-create the env secrets provider (check mode) + sensu.sensu_go.secrets_provider_env: *create-provider + check_mode: true + register: result + + - ansible.builtin.assert: &create-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'env' + + - name: Make sure no secrets providers were created when running in check mode + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Re-create the env secrets provider + sensu.sensu_go.secrets_provider_env: *create-provider + register: result + + - ansible.builtin.assert: *create-provider-assertions + + - name: Make sure env secrets provider was really created + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: env + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'env' diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml new file mode 100644 index 000000000..a5971afc4 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml @@ -0,0 +1,272 @@ +--- +- name: Pre-converge step - ensure presence of valid PEM files for TLS configuration + hosts: all + gather_facts: no + tasks: + # As part of configuring TLS for the vault secrets provider, we need + # to specify paths to CA cert and client cert/key files. + # If these files do not exist at the specified paths on SensuGo backend, + # or are not valid PEM files, the API returns error 500. + - name: Copy PEM files to Sensu backend + copy: + src: "files/{{ item }}" + dest: "/tmp/{{ item }}" + mode: 0744 + loop: + - ca.crt + - client.crt + - client.key + +- name: Converge + hosts: all + gather_facts: no + tasks: + - name: Fetch all secrets providers and verify the presence of the + default env provider + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 # env provider is present by default + - result.objects.0.metadata.name == 'env' + + - name: Fail when trying to create a vault provider with missing required params + sensu.sensu_go.secrets_provider_vault: + auth: + url: http://localhost:8080 + name: my-vault + register: result + ignore_errors: true + + - ansible.builtin.assert: + that: + - result is failed + - "'state is present but all of the following are missing' in result.msg" + + - name: Create a vault provider with minimal params (check mode) + sensu.sensu_go.secrets_provider_vault: &create-provider + auth: + url: http://localhost:8080 + name: my-vault + address: https://my-vault.com + token: VAULT_TOKEN + version: v1 + check_mode: true + register: result + + - ansible.builtin.assert: &create-provider-assertions + that: + - result is changed + - result.object.metadata.name == 'my-vault' + - result.object.client.address == 'https://my-vault.com' + - result.object.client.version == 'v1' + + - name: Make sure vault provider was not created when running in check mode + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'env' + + - name: Create a vault provider with minimal params + sensu.sensu_go.secrets_provider_vault: *create-provider + register: result + + - ansible.builtin.assert: *create-provider-assertions + + - name: Make sure vault secrets provider was created + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: my-vault + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'my-vault' + + - name: Idempotence check for vault provider creation with minimal params + sensu.sensu_go.secrets_provider_vault: *create-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Update vault secrets provider (check mode) + sensu.sensu_go.secrets_provider_vault: &update-provider + auth: + url: http://localhost:8080 + name: my-vault + address: https://my-new-vault.com + token: ANOTHER_VAULT_TOKEN + version: v2 + timeout: 5 + max_retries: 1 + rate_limit: 15.3 + burst_limit: 50 + tls: + ca_cert: /tmp/ca.crt + check_mode: true + register: result + + - ansible.builtin.assert: &update-provider-assertions + that: + - result is changed + - result.object.client.address == 'https://my-new-vault.com' + - result.object.client.version == 'v2' + - result.object.client.timeout == '5s' + - result.object.client.max_retries == 1 + - result.object.client.rate_limiter.limit == 15.3 + - result.object.client.rate_limiter.burst == 50 + - result.object.client.tls.ca_cert == '/tmp/ca.crt' + + - name: Make sure vault secrets provider was not updated in check mode + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: my-vault + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.metadata.name == 'my-vault' + - result.objects.0.client.address == 'https://my-vault.com' + - result.objects.0.client.version == 'v1' + - result.objects.0.client.tls == None + + - name: Update vault secrets provider + sensu.sensu_go.secrets_provider_vault: *update-provider + register: result + + - ansible.builtin.assert: *update-provider-assertions + + - name: Make sure vault secrets provider was updated + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: my-vault + register: result + + - ansible.builtin.assert: + that: + - result.objects.0.client.address == 'https://my-new-vault.com' + - result.objects.0.client.version == 'v2' + - result.objects.0.client.timeout == '5s' + - result.objects.0.client.max_retries == 1 + - result.objects.0.client.rate_limiter.limit == 15.3 + - result.objects.0.client.rate_limiter.burst == 50 + - result.objects.0.client.tls.ca_cert == '/tmp/ca.crt' + + - name: Idempotence check for vault provider modification + sensu.sensu_go.secrets_provider_vault: *update-provider + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Create a vault secrets provider with all params + sensu.sensu_go.secrets_provider_vault: &create-provider-all-params + auth: + url: http://localhost:8080 + name: my-other-vault + address: https://my-other-vault.com + token: OTHER_VAULT_TOKEN + version: v1 + timeout: 30 + max_retries: 1 + rate_limit: 5.2 + burst_limit: 90 + tls: + ca_cert: /tmp/ca.crt + client_cert: /tmp/client.crt + client_key: /tmp/client.key + cname: my-vault.com + register: result + + - ansible.builtin.assert: + that: + - result is changed + - result.object.metadata.name == 'my-other-vault' + - result.object.client.address == 'https://my-other-vault.com' + - result.object.client.version == 'v1' + - result.object.client.timeout == '30s' + - result.object.client.max_retries == 1 + - result.object.client.rate_limiter.limit == 5.2 + - result.object.client.rate_limiter.burst == 90 + - result.object.client.tls.ca_cert == '/tmp/ca.crt' + - result.object.client.tls.client_cert == '/tmp/client.crt' + - result.object.client.tls.client_key == '/tmp/client.key' + - result.object.client.tls.cname == 'my-vault.com' + + - name: Idempotence check for vault provider creation with all params + sensu.sensu_go.secrets_provider_vault: *create-provider-all-params + register: result + + - ansible.builtin.assert: + that: + - result is not changed + + - name: Delete a vault secrets provider (check mode) + sensu.sensu_go.secrets_provider_vault: &delete-provider + auth: + url: http://localhost:8080 + name: my-vault + state: absent + check_mode: true + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure vault secrets provider was not deleted when running in check mode + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: my-vault + register: result + + - ansible.builtin.assert: + that: + - result.objects | length == 1 + + - name: Delete a vault secrets provider + sensu.sensu_go.secrets_provider_vault: *delete-provider + register: result + + - ansible.builtin.assert: + that: + - result is changed + + - name: Make sure vault secrets provider was deleted + sensu.sensu_go.secrets_provider_info: + auth: + url: http://localhost:8080 + name: my-vault + register: result + + - ansible.builtin.assert: + that: + - result.objects == [] + + - name: Delete a non-existent vault secrets provider + sensu.sensu_go.secrets_provider_vault: + auth: + url: http://localhost:8080 + name: i-dont-exist + state: absent + register: result + + - ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt new file mode 100644 index 000000000..ceffe5219 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCDCCAfCgAwIBAgITMgYuhrafZc78Bk5PXip8xwQqTjANBgkqhkiG9w0BAQsF +ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjAxMDA3MDYzMzQzWhcNNDAwMTI2 +MDYzMzQzWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCk8LHitgJipeUaqWW2dsMe44TiT6XO17pPNJwRLFA7eSEN +Vn5gCNd2yfEjsx7lXaZApghwz0YIE6gzkvFPS5cHCbPuBqyI6rSUPEXvMdkt7EGG +40uhcX/otp0FvQr+Uvqo9NQyavTrgEPTudUptLFJd8QkZaVzprx061K2uV8Kjc4h +0MDIX5+4amb+h4eEBloTjH4lc6INe2uezAodiRjUp/TOYJzr5OIPYd3jjbbB1G3T +kwsQEz9qiEEwHN8AUGEFWqIy2O3BNTbS4D5Fv4n3U+HSFQdB5ORHGKbu/s0e8Tr8 +K5udPjM+ld8PazD2UIBBpWDbAqYvUrdTxYhxFqIrAgMBAAGjUzBRMB0GA1UdDgQW +BBS6M9F187X6KhlndKvdTNxC25xRrzAfBgNVHSMEGDAWgBS6M9F187X6KhlndKvd +TNxC25xRrzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZvMa4 +R+AqR3lR3zpzFtN6OK/oFbUTloOE6+sWkFlE197YlvaUK0NmD67Xlvuor++KfCci +ru2nOhUkqYRHQ5a/ZACEOhv0PyCcTTAsEWcjs/I/zBTxvzJUjX7o6X1wtVmo5lI5 +lLpru+5h5XsqHMnIbdqfGKJup3j44d0qUDUrVKAsS8ioWvmuyZyxczeKvb+CvQLq +sNPSD+7H8l6WdWfo0unmCz7FkZDtSg9LCjzMF0TEe5D9ApWb6GtLy1XRKzg8jGzG +MuuzNzlaqBgWI88HRL25ZkVLwNahb7dkkt82d8ghdmtkisXHm24BHwPnXOpQ2LGn +ZPUwbq0OkVDt9nkr +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt new file mode 100644 index 000000000..b6c5fe9fc --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUbhKhIYBliY9SvDegW9ND5+YAQSowDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEwMDYwOTMwMjZaFw0zMDEw +MDQwOTMwMjZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDApjHtQI+w5Lu2uoMMo+iAcVb+6PjzWVL1G34d31HK +hZynEPbCkJzpXsUOnbPagWmTXFMBblqgE1Pj4zT97oqWPBlIV3UA5M7rBQel76Vq +YPKNDcZ8fGtCaL3wFFmN3ou6QaI+Zy3CJAy6Q71426xRawHWCgF7in3k90bVw3Qy +hrW8VrEgcwobdHn79W9idx+OBYcku5Fn1ciZn9RQoL0GRIRsg5IyB0Uy9W1Vyc74 +Bp2LTySn4QwPflfCjb9+83oeMTwPTDU3RHPVbMVoL87+jXD5TeZTCNooIYDRYjP6 +3gWEQMVL+LJpaFZvdp26uArJnCu69wgyvpzGqnExKgl3AgMBAAGjUzBRMB0GA1Ud +DgQWBBRBlAIaZUvLzoGXdIfOXulVTGIg0zAfBgNVHSMEGDAWgBRBlAIaZUvLzoGX +dIfOXulVTGIg0zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBG +U/Xnzm5+H6gXzq8hvFBIeaMYQmTgLwKt0w7yHBSQSNJC4qouIPgbyKKwrQJOvdI3 +/q3GRTkzMB/GKHRwh9nE4Z9wBLLY8b5K2t2yyQjpNtHyKeO+cc3E1gKSa0/QMEHh +OLtQsCixfACb3U0KizhwocACQ52NVMd2LhBuO4UCAGi1I5SFZzrgIcrG9WoB1Qzx +YoWh/49BL45LuLQ9MvPxya6ZuE0Pg38xRr+5yaEbnEAinVGy5eL+PNa5aMPogUB3 ++hINK/Ous3ab0zJyJ+1lN/9ijx43R2RfCo8GXA7x9Wz+N+nW8MAJbICiJR8pljiy +GFQiUdAX24flMYN14ZjX +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key new file mode 100644 index 000000000..d22f68713 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDApjHtQI+w5Lu2 +uoMMo+iAcVb+6PjzWVL1G34d31HKhZynEPbCkJzpXsUOnbPagWmTXFMBblqgE1Pj +4zT97oqWPBlIV3UA5M7rBQel76VqYPKNDcZ8fGtCaL3wFFmN3ou6QaI+Zy3CJAy6 +Q71426xRawHWCgF7in3k90bVw3QyhrW8VrEgcwobdHn79W9idx+OBYcku5Fn1ciZ +n9RQoL0GRIRsg5IyB0Uy9W1Vyc74Bp2LTySn4QwPflfCjb9+83oeMTwPTDU3RHPV +bMVoL87+jXD5TeZTCNooIYDRYjP63gWEQMVL+LJpaFZvdp26uArJnCu69wgyvpzG +qnExKgl3AgMBAAECggEAeP9pWEQ2e7oOFESszqGcBCArrcsRoXY23m+4FHcQ3gxx +SZUkByvhAcpeJkHyloi1kLJqB/oRvXymMfmgbOUH8jgpAQC9IiSTuZTdKuGLmXbu +oJ1ITyOuEnXK5iFB5hMi24chqeQQH2GZBNxLAr+mSTTWYGLEb52aWNbejKBIOPq9 +xnHD3gkFwod5dQt9ZY0bh+jTdM6cRuVyUooxbkFIkblHDQ8vEWsdC+b4rAXVH71e +JjrnI4y/4zDdF5akexpaHrva+J1Y2PCkdFvfO4drl5XKysSiUMX37XN32LxU5/ON +c6umKQnot1GL4ognOQw5HcygkT2Y8ltVbdcEK21xYQKBgQD7e1J48DYvpOhVVMaY +fqV2RuXKpoyy+7RIU+YrxaduwJYqH6ZWnLOYf2ZtlKRVsuHw+4fjAxw4DW6FzkMA +hXcgFwbLQO97dcxETPcq7Hny7mSQy8PO8iLXMHqOerxo16DNivc6Kw5NV+S/5XaU +IMNCmNl+1ZzkdNhUjeknV5Sf/wKBgQDEHEaKXAQoxGffRFoZK0aj3FX8N+qsYn4c +mftCkWZzKLMvwgjt0CXsHqrx9C3WecF2yrTGyMEsuI6eOBWVLqfkTUH9JgcjeFiF +R2c25zBS28WZtZwFaIi3oUtXHX2rKyWza3rQZSqEgXXV+ck/Ks7LFqpuHmttnYHt +qTXNIx+WiQKBgBpsXMAETU04QIkmvS8sr2n8DQz77vCnbcvjtN2IiQ0kAyMt7CZR +lLVDPZnp8lJm10KgyyhZHU/uaVx5zaRyYY/nm3kju4X3XJ0YkSfbbPzPe5WTM2G9 +I1gE6fuqfb1uWqD+JvffqkMKJyjajVkHED0hHkkrXK7McCaCOqs9koo1AoGBAIEN +NmJgUSBetxgWi8/aSZ8VJMRYK6cLHYBG2DCjLC8GDnyUDHoqqnnqaIXWML/d2bEJ +jdLuUyjRvpBhydolHLjBGnazKqltzZrPNR3NH2C3XR5cg3KPqsBkdQa70nHsb9/V +D7nJiSQvaVLJEGTwD6tXnAnhHMLCjrjNzCjVPzk5AoGBANHxGdKUaZivQV3dMo3p +6WHfwe1rTdaiahvEHH2wi4ch+m7b92p+ey2aPg8kvnHKh8TG0NuAcJZ1/lG2qex8 +89pmdwaqkggi/ApPR0vvZiIQgSawlPW3pg1VAW/DtzYGAJlGWwbI212szN94MDYH +t31cQ5V1qUo1rJaQE0rM0OwB +-----END PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml new file mode 100644 index 000000000..20496d0e8 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml @@ -0,0 +1,180 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create silence without parameters + silence: + auth: + url: http://localhost:8080 + ignore_errors: True + register: result + + - assert: + that: + - result is failed + - "result.msg == 'one of the following is required: subscription, check'" + + - name: Create silence with only check parameter + silence: + auth: + url: http://localhost:8080 + check: check + register: result + + - assert: + that: + - result is changed + - result.object.check == 'check' + - result.object.metadata.name == '*:check' + + - name: Create silence with only check parameter idempotence + silence: + auth: + url: http://localhost:8080 + check: check + register: result + + - assert: + that: + - result is not changed + + - name: Create silence with only subscription parameter + silence: + auth: + url: http://localhost:8080 + subscription: agent + register: result + + - assert: + that: + - result is changed + - result.object.subscription == 'agent' + - result.object.metadata.name == 'agent:*' + + - name: Create silence with only subscription parameter idempotence + silence: + auth: + url: http://localhost:8080 + subscription: agent + register: result + + - assert: + that: + - result is not changed + + - name: Create a silence + silence: &idempotence + auth: + url: http://localhost:8080 + subscription: entity:mail-server + check: some-check + # Schedule silence in the future (2030) in order to prevent it from + # expiring and getting dropped on the floor by the backend. + begin: 1893452400 + expire: 120 + expire_on_resolve: True + reason: updating mail server + register: result + + - assert: + that: + - result is changed + - result.object.metadata.name == 'entity:mail-server:some-check' + - result.object.subscription == 'entity:mail-server' + - result.object.check == 'some-check' + - result.object.begin == 1893452400 + - result.object.expire == 120 + - result.object.expire_on_resolve == True + - result.object.creator == 'admin' + - result.object.reason == 'updating mail server' + + - name: Test silence creation idempotence + silence: *idempotence + register: result + + - assert: + that: result is not changed + + - name: Modify a silence + silence: + auth: + url: http://localhost:8080 + subscription: entity:mail-server + check: some-check + begin: 1893452500 + register: result + + - assert: + that: + - result is changed + - result.object.begin == 1893452500 + - result.object.subscription == 'entity:mail-server' + - result.object.check == 'some-check' + - not result.object.expire_on_resolve + + - name: Create a second silence + silence: + auth: + url: http://localhost:8080 + subscription: agent + check: check-cpu + + - name: Fetch all silences + silence_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 4 + - result.objects.0.check == 'check' + - "'subscription' not in result.objects.0" + + - name: Fetch a specific silence + silence_info: + auth: + url: http://localhost:8080 + subscription: agent + check: check-cpu + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.subscription == 'agent' + - result.objects.0.check == 'check-cpu' + + - name: Delete a silence + silence: + auth: + url: http://localhost:8080 + check: check + state: absent + + - name: Fetch all silecnes + silence_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 3 + - result.objects.0.subscription == 'agent' + - "'check' not in result.objects.0" + + - name: Try to fetch non-existing silence + silence_info: + auth: + url: http://localhost:8080 + subscription: bad + check: bad + register: result + + - assert: + that: + - result.objects == [] diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml new file mode 100644 index 000000000..8dcb5fc11 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml @@ -0,0 +1,153 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Create socket handler with missing required parameters + socket_handler: + auth: + url: http://localhost:8080 + name: handler + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'state is present but all of the following are missing: type, host, port'" + + - name: Create socket handler with minimal parameters + socket_handler: + auth: + url: http://localhost:8080 + name: minimal_handler + type: tcp + host: 10.1.0.99 + port: 4444 + register: result + + - assert: + that: + - result is changed + - result.object.socket.host == '10.1.0.99' + - result.object.socket.port == 4444 + - result.object.type == 'tcp' + - result.object.metadata.name == 'minimal_handler' + + - name: Create socket handler with minimal parameters idempotence + socket_handler: + auth: + url: http://localhost:8080 + name: minimal_handler + type: tcp + host: 10.1.0.99 + port: 4444 + register: result + + - assert: + that: result is not changed + + - name: Create an socket handler with full parameters + socket_handler: + auth: + url: http://localhost:8080 + name: handler + type: udp + mutator: mutate_input + timeout: 30 + filters: + - occurances + - production + host: 10.1.0.99 + port: 4444 + register: result + + - assert: + that: + - result is changed + - result.object.socket.host == '10.1.0.99' + - result.object.socket.port == 4444 + - result.object.type == 'udp' + - result.object.mutator == 'mutate_input' + - result.object.timeout == 30 + - result.object.filters == ['occurances', 'production'] + + - name: Create socket handler with full parameters idempotence + socket_handler: + auth: + url: http://localhost:8080 + name: handler + type: udp + mutator: mutate_input + timeout: 30 + filters: + - occurances + - production + host: 10.1.0.99 + port: 4444 + register: result + + - assert: + that: result is not changed + + - name: Modify socket handler + socket_handler: + auth: + url: http://localhost:8080 + name: handler + type: tcp + timeout: 60 + host: 10.1.0.99 + port: 4444 + register: result + + - assert: + that: + - result is changed + - result.object.type == 'tcp' + - result.object.socket.host == '10.1.0.99' + - result.object.socket.port == 4444 + - not result.object.filters + - "'mutator' not in result.object" + + - name: Fetch all socket handlers + handler_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 2 + + - name: Fetch a specific socket handler + handler_info: + auth: + url: http://localhost:8080 + name: handler + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'handler' + + - name: Delete socket handler + socket_handler: + auth: + url: http://localhost:8080 + name: handler + state: absent + + - name: Get all socket handlers + handler_info: + auth: + url: http://localhost:8080 + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.metadata.name == 'minimal_handler'
\ No newline at end of file diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml new file mode 100644 index 000000000..a06da3054 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml @@ -0,0 +1,53 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + tasks: + - name: Call tessen with missing required parameters + tessen: + auth: + url: http://localhost:8080 + ignore_errors: true + register: result + + - assert: + that: + - result is failed + - "result.msg == 'missing required arguments: state'" + + - name: Disable tessen # Tessen is enabled by default on Sensu backends + tessen: + auth: + url: http://localhost:8080 + state: disabled + register: result + + - assert: + that: + - result is changed + - result.object.opt_out == True + + - name: Enable tessen + tessen: + auth: + url: http://localhost:8080 + state: enabled + register: result + + - assert: + that: + - result is changed + - result.object.opt_out == False + + - name: Try to enable already enabled tessen + tessen: + auth: + url: http://localhost:8080 + state: enabled + register: result + + - assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml new file mode 100644 index 000000000..7312779e6 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml @@ -0,0 +1,198 @@ +--- +- name: Converge + collections: + - sensu.sensu_go + hosts: all + gather_facts: no + environment: + SENSU_URL: http://localhost:8080 + + tasks: + - name: Create user with minimal parameters + user: + name: awesome_username + password: hidden_password? + register: result + + - assert: + that: + - result is changed + - result.object.username == 'awesome_username' + + - name: Minimal parameter idempotence check + user: + name: awesome_username + password: hidden_password? + register: result + + - assert: + that: + - result is not changed + + - name: Disable enabled user + user: + name: awesome_username + state: disabled + register: result + + - assert: + that: + - result.object.disabled == True + + - name: Add disabled user to some groups + user: + name: awesome_username + groups: [ a, b, c ] + state: disabled + register: result + + - assert: + that: + - result is changed + - result.object.groups | sort == ['a', 'b', 'c'] + + - name: Change password on disabled user + user: + name: awesome_username + password: new_pass + state: disabled + register: result + + - assert: + that: + - result is changed + + - name: Create a disabled user + user: + name: test_username + password: hidden_password? + state: disabled + groups: + - dev + - prod + register: result + + - assert: + that: + - result is changed + - result.object.username == 'test_username' + - result.object.disabled == True + - result.object.groups == ['dev', 'prod'] + + - name: Try to disable an already disabled user + user: + name: test_username + state: disabled + register: result + + - assert: + that: + - result is not changed + + - name: Enable a disabled user + user: + name: test_username + register: result + + - assert: + that: + - result is changed + - result.object.disabled == False + + - name: Modify a user + user: + name: test_username + password: hidden_password! + groups: + - dev + register: result + + - assert: + that: + - result is changed + - result.object.groups == ['dev'] + + - name: Fetch all users + user_info: + register: result + + - debug: + var: result + + - assert: + that: + - result.objects | length == 4 # users admin and agent already exist + + - name: Fetch specific user + user_info: + name: test_username + register: result + + - assert: + that: + - result.objects | length == 1 + - result.objects.0.username == 'test_username' + - result.objects.0.disabled == False + + - name: Try to create a user with no password + user: + name: missing_user + state: disabled + ignore_errors: true + register: result + + - assert: + that: + - result is failed + + - name: Try to fetch non-existing user + user_info: + name: bad-bad-user + register: result + + - assert: + that: + - result.objects == [] + + - name: Create a user with password hash (check mode) + user: &hash_pass_user + name: hash_pass_user + password_hash: $5f$14$.brXRviMZpbaleSq9kjoUuwm67V/s4IziOLGHjEqxJbzPsreQAyNm + check_mode: true + register: result + - assert: + that: + - result is changed + - result.object.username == "hash_pass_user" + + - name: Make sure nothing changed + user_info: + name: hash_pass_user + register: result + - assert: + that: + - result.objects | length == 0 + + - name: Create a user with password hash + user: *hash_pass_user + register: result + - assert: + that: + - result is changed + - result.object.username == "hash_pass_user" + + - name: Make sure new user appeared + user_info: + name: hash_pass_user + register: result + - assert: + that: + - result.objects | length == 1 + - result.objects.0.username == "hash_pass_user" + + - name: Create a user with password hash (failed idempotence for hash) + user: *hash_pass_user + register: result + - assert: + that: + - result is changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml new file mode 100644 index 000000000..f00ffa71c --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml @@ -0,0 +1,134 @@ +- name: Converge + hosts: all + + tasks: + - name: Configure overloaded variables + include_role: + name: sensu.sensu_go.agent + tasks_from: configure + vars: + agent_config: + backend-url: + - "ws://0.0.0.0:4321" + + - name: Confirm overloaded variables + slurp: + src: /etc/sensu/agent.yml + register: agent_yml + + - assert: + that: + - | + agent_yml.content | b64decode | from_yaml == { + "backend-url": ["ws://0.0.0.0:4321"] + } + + - name: Configure full set of optional variables + include_role: + name: sensu.sensu_go.agent + tasks_from: configure + vars: + agent_config: + annotations: + example-key: "example value" + example-key2: "example value 2" + backend-url: + - "ws://0.0.0.0:4321" + cache-dir: /tmp/sensu-agent/cache + disable-assets: true + allow-list: /etc/sensu/check-allow-list.yaml + label: + example_label1: "example label" + example_label2: "example label 2" + name: "agent-01" + log-level: fatal + api-hosts: "192.168.10.3" + api-port: 3031 + disable-api: true + events-burst-limit: 20 + events-rate-limit: 13.4 + deregister: false + deregister-handler: "deregister" + keepalive-interval: 35 + keepalive-timeout: 215 + namespace: "ops" + user: "sensuadmin" + password: "Never2GuessM3" + redact: + - secret_key + - usernames + - passwords + trusted-ca-file: "/var/lib/sensu/ca.pem" + insecure-skip-tls-verify: true + socket-host: "0.0.0.0" + socket-port: 13030 + disable-sockets: true + statsd-disable: true + statsd-event-handlers: + - influxdb + - opentsdb + statsd-flush-interval: 18 + statsd-metrics-host: 192.168.50.60 + statsd-metrics-port: 18125 + exec: "/usr/local/bin/check_disk.sh" + sha512: b648feb599b35722248e3d0b0c43ba1c60e4531e9547552e552e7b8dfdb54bb8e1ea414cf33b366ec0a51dd1423bd9a1846b9338771ab41d02a98695d9577834 + args: + - /dev/sda1 + - /dev/sda2 + - "-r" + enable_env: false + + - name: Confirm full set of optional variables + slurp: + src: /etc/sensu/agent.yml + register: agent_yml + + - debug: + var: agent_yml.content | b64decode | from_yaml + + - assert: + that: + - | + agent_yml.content | b64decode | from_yaml == { + "allow-list": "/etc/sensu/check-allow-list.yaml", + "annotations": { + "example-key": "example value", + "example-key2": "example value 2", + }, + "api-hosts": "192.168.10.3", + "api-port": 3031, + "args": ["/dev/sda1", "/dev/sda2", "-r"], + "backend-url": ["ws://0.0.0.0:4321"], + "cache-dir": "/tmp/sensu-agent/cache", + "deregister": False, + "deregister-handler": "deregister", + "disable-api": True, + "disable-assets": True, + "disable-sockets": True, + "enable_env": False, + "events-burst-limit": 20, + "events-rate-limit": 13.4, + "exec": "/usr/local/bin/check_disk.sh", + "insecure-skip-tls-verify": True, + "keepalive-interval": 35, + "keepalive-timeout": 215, + "label": { + "example_label1": "example label", + "example_label2": "example label 2", + }, + "log-level": "fatal", + "name": "agent-01", + "namespace": "ops", + "password": "Never2GuessM3", + "redact": ["secret_key", "usernames", "passwords"], + "sha512": "b648feb599b35722248e3d0b0c43ba1c60e4531e9547552e552e7b8dfdb54bb8e1ea414cf33b366ec0a51dd1423bd9a1846b9338771ab41d02a98695d9577834", + "socket-host": "0.0.0.0", + "socket-port": 13030, + "statsd-disable": True, + "statsd-event-handlers": ["influxdb", "opentsdb"], + "statsd-flush-interval": 18, + "statsd-metrics-host": "192.168.50.60", + "statsd-metrics-port": 18125, + "trusted-ca-file": "/var/lib/sensu/ca.pem", + "user": "sensuadmin", + } diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml new file mode 100644 index 000000000..7ba0b239f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml @@ -0,0 +1,14 @@ +--- +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - destroy + +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml new file mode 100644 index 000000000..40ba62238 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml @@ -0,0 +1,24 @@ +--- +- name: Prepare + hosts: all + + tasks: + - name: Create sensu group + group: + name: sensu + + - name: Create sensu user + # We need FQCN here because we are running test from within the + # collection. In this case, our collection becomes the default + # collection and so the sensu.sensu_go.user module shadows the builtin + # one. + ansible.builtin.user: + name: sensu + groups: sensu + + - name: Create /etc/sensu folder + file: + state: directory + path: /etc/sensu + owner: sensu + group: sensu diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml new file mode 100644 index 000000000..f56054f04 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: agents + roles: + - sensu.sensu_go.agent diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml new file mode 100644 index 000000000..505b22632 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml @@ -0,0 +1,30 @@ +--- +scenario: + test_sequence: + - destroy + - create + - converge + - verify + - check + - destroy + +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true + groups: [ agents ] + override_command: false + privileged: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + +# Upstream container - just to populate the inventory +provisioner: + inventory: + hosts: + all: + children: + backends: + hosts: + upstream-backend: diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml new file mode 100644 index 000000000..bbe821561 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml @@ -0,0 +1,28 @@ +--- +- name: Verify + hosts: agents + + tasks: + - name: Agent configuration must exist + stat: + path: /etc/sensu/agent.yml + register: result + + - assert: + that: + - result.stat.exists + - result.stat.mode == '0600' + - result.stat.pw_name == 'sensu' + - result.stat.gr_name == 'sensu' + + - name: Confirm default configuration settings + slurp: + src: /etc/sensu/agent.yml + register: agent_yml + + - assert: + that: + - | + agent_yml.content | b64decode | from_yaml == { + "backend-url": ["ws://upstream-backend:8081"], + } diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml new file mode 100644 index 000000000..7e0e1ccfa --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml @@ -0,0 +1,71 @@ +--- +- name: Pre-converge secure backend config step + hosts: backends + gather_facts: no + tasks: + - name: Set dummy backend PKI variables + set_fact: + api_key_file: /tmp/dummy.key + + - debug: + msg: "{{ hostvars['upstream-backend']['api_key_file'] }}" + +- name: Converge + hosts: agents + vars: + agent_trusted_ca_file: files/sensu-agent-trusted-ca.crt + roles: + - sensu.sensu_go.agent + +- name: Verify configure_agent + hosts: agents + tasks: + - name: The trusted CA store file must exist + stat: + path: /etc/sensu/sensu-agent-trusted-ca.crt + register: result + + - assert: + that: + - "{{ result.stat.exists }}" + - "{{ result.stat.pw_name == 'sensu' }}" + - "{{ result.stat.gr_name == 'sensu' }}" + - "{{ result.stat.mode == '0644' }}" + + - name: Confirm secured agent configuration settings + lineinfile: + path: &agent_yml /etc/sensu/agent.yml + line: '{{ item }}' + with_items: + - 'backend-url:' + - '- wss://upstream-backend:8081' + - 'trusted-ca-file: /etc/sensu/sensu-agent-trusted-ca.crt' + - 'insecure-skip-tls-verify: false' + register: result + + - assert: + that: + - result is not changed + +- name: Default configuration + hosts: agents + gather_facts: no + roles: + - sensu.sensu_go.agent + +- name: Verify default configuration + hosts: agents + tasks: + - name: Confirm that none of secured configuration settings leak in + lineinfile: + path: *agent_yml + regexp: '{{ item }}' + state: absent + with_items: + - '^trusted-ca-file:' + - '^insecure-skip-tls-verify:' + register: result + + - assert: + that: + - result is not changed diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt new file mode 100644 index 000000000..fe9c08f48 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y +OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3 +8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS +KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u +53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb +7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg +vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw +gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv +NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB +ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in +NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m +tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K +3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ +RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ +6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml new file mode 100644 index 000000000..430054e8b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml @@ -0,0 +1,21 @@ +--- +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true + groups: [ agents ] + override_command: false + privileged: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + +# Upstream container - just to populate the inventory +provisioner: + inventory: + hosts: + all: + children: + backends: + hosts: + upstream-backend: diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml new file mode 100644 index 000000000..056998447 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml @@ -0,0 +1,74 @@ +--- +- name: Converge + hosts: all + + tasks: + - name: Configure select set of optional variables + include_role: + name: sensu.sensu_go.backend + tasks_from: configure + vars: + backend_config: + debug: no + log-level: debug + api-listen-address: "[::]:4430" + + - name: Confirm optional configuration settings + slurp: + src: /etc/sensu/backend.yml + register: backend_yml + + - assert: + that: + - | + backend_yml.content | b64decode | from_yaml == { + "state-dir": "/var/lib/sensu/sensu-backend", + "debug": False, + "log-level": "debug", + "api-listen-address": "[::]:4430", + } + + - name: Configure full set of optional variables + include_role: + name: sensu.sensu_go.backend + tasks_from: configure + vars: + backend_config: + debug: yes + log-level: debug + state-dir: /tmp/different/state + deregistration-handler: /tmp/handler.sh + agent-host: "127.0.0.1" + agent-port: 4431 + api-listen-address: "[::]:4430" + api-url: "http://localhost:4432" + dashboard-host: "192.168.10.6" + dashboard-port: 4433 + etcd-initial-advertise-peer-urls: + - https://10.10.0.1:2380 + - https://10.20.0.1:2380 + + - name: Confirm full configuration settings + slurp: + src: /etc/sensu/backend.yml + register: backend_yml + + - assert: + that: + - | + backend_yml.content | b64decode | from_yaml == { + "debug": True, + "log-level": "debug", + "state-dir": "/tmp/different/state", + "deregistration-handler": "/tmp/handler.sh", + "agent-host": "127.0.0.1", + "agent-port": 4431, + "api-listen-address": "[::]:4430", + "api-url": "http://localhost:4432", + "dashboard-host": "192.168.10.6", + "dashboard-port": 4433, + "etcd-initial-advertise-peer-urls": [ + "https://10.10.0.1:2380", + "https://10.20.0.1:2380", + ] + } diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml new file mode 100644 index 000000000..7ba0b239f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml @@ -0,0 +1,14 @@ +--- +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - destroy + +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml new file mode 100644 index 000000000..40ba62238 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml @@ -0,0 +1,24 @@ +--- +- name: Prepare + hosts: all + + tasks: + - name: Create sensu group + group: + name: sensu + + - name: Create sensu user + # We need FQCN here because we are running test from within the + # collection. In this case, our collection becomes the default + # collection and so the sensu.sensu_go.user module shadows the builtin + # one. + ansible.builtin.user: + name: sensu + groups: sensu + + - name: Create /etc/sensu folder + file: + state: directory + path: /etc/sensu + owner: sensu + group: sensu diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml new file mode 100644 index 000000000..69becbbfa --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + + tasks: + - name: Install backend + include_role: + name: sensu.sensu_go.backend diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml new file mode 100644 index 000000000..f9c28ce1a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml @@ -0,0 +1,20 @@ +--- +scenario: + test_sequence: + - destroy + - create + - converge + - idempotence + - verify + - check + - destroy + +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true + override_command: false + privileged: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml new file mode 100644 index 000000000..c6d487eaa --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml @@ -0,0 +1,35 @@ +--- +- name: Verify + hosts: all + + tasks: + - name: Backend configuration must exist + stat: + path: /etc/sensu/backend.yml + register: result + + - assert: + that: + - result.stat.exists + - result.stat.mode == '0600' + - result.stat.pw_name == 'sensu' + - result.stat.gr_name == 'sensu' + + - name: Confirm default configuration settings + slurp: + src: /etc/sensu/backend.yml + register: backend_yml + + - assert: + that: + - | + backend_yml.content | b64decode | from_yaml == { + "state-dir": "/var/lib/sensu/sensu-backend", + } + + - name: Make sure login works + uri: + url: http://localhost:8080/auth + url_username: admin + url_password: P@ssw0rd! + force_basic_auth: yes diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml new file mode 100644 index 000000000..fd6861879 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml @@ -0,0 +1,164 @@ +--- +- name: Converge + hosts: all + + tasks: + - name: Install secured backend + include_role: + name: sensu.sensu_go.backend + tasks_from: configure + vars: + etcd_cert_file: files/etcd-client.crt + etcd_key_file: files/etcd-client.key + etcd_trusted_ca_file: files/client-ca.crt + etcd_peer_cert_file: files/etcd-peer.crt + etcd_peer_key_file: files/etcd-peer.key + etcd_peer_trusted_ca_file: files/etcd-peer-ca.crt + api_cert_file: files/sensu-api.crt + api_key_file: files/sensu-api.key + api_trusted_ca_file: files/sensu-api-ca.crt + dashboard_cert_file: files/sensu-dashboard.crt + dashboard_key_file: files/sensu-dashboard.key + + - name: The public keys must exist + stat: + path: "{{ item }}" + register: result + loop: + - /etc/sensu/etcd-client.crt + - /etc/sensu/etcd-client-ca.crt + - /etc/sensu/etcd-peer.crt + - /etc/sensu/etcd-peer-ca.crt + - /etc/sensu/api.crt + - /etc/sensu/api-ca.crt + - /etc/sensu/dashboard.crt + + - assert: + that: + - item.stat.exists + - item.stat.pw_name == 'sensu' + - item.stat.gr_name == 'sensu' + - item.stat.mode == '0644' + loop: "{{ result.results }}" + + - name: The private keys must exist and be protected + stat: + path: "{{ item }}" + register: result + loop: + - /etc/sensu/etcd-client.key + - /etc/sensu/etcd-peer.key + - /etc/sensu/api.key + - /etc/sensu/dashboard.key + + - assert: + that: + - item.stat.exists + - item.stat.pw_name == 'sensu' + - item.stat.gr_name == 'sensu' + - item.stat.mode == '0400' + loop: "{{ result.results }}" + + - name: Confirm secured configuration settings + slurp: + src: /etc/sensu/backend.yml + register: backend_yml + + - assert: + that: + - | + backend_yml.content | b64decode | from_yaml == { + "state-dir": "/var/lib/sensu/sensu-backend", + "etcd-listen-client-urls": "https://localhost:2379", + "etcd-listen-peer-urls": "https://localhost:2380", + "etcd-initial-advertise-peer-urls": "https://localhost:2380", + "etcd-initial-cluster": "default=https://localhost:2380", + "etcd-cert-file": "/etc/sensu/etcd-client.crt", + "etcd-key-file": "/etc/sensu/etcd-client.key", + "etcd-trusted-ca-file": "/etc/sensu/etcd-client-ca.crt", + "etcd-client-cert-auth": True, + "etcd-peer-cert-file": "/etc/sensu/etcd-peer.crt", + "etcd-peer-key-file": "/etc/sensu/etcd-peer.key", + "etcd-peer-client-cert-auth": True, + "etcd-peer-trusted-ca-file": "/etc/sensu/etcd-peer-ca.crt", + "cert-file": "/etc/sensu/api.crt", + "key-file": "/etc/sensu/api.key", + "trusted-ca-file": "/etc/sensu/api-ca.crt", + "insecure-skip-tls-verify": False, + "api-url": "https://localhost:8080", + "dashboard-cert-file": "/etc/sensu/dashboard.crt", + "dashboard-key-file": "/etc/sensu/dashboard.key", + } + + - name: Configure an overriding of managed vars + include_role: + name: sensu.sensu_go.backend + tasks_from: configure + vars: + etcd_cert_file: files/etcd-client.crt + etcd_key_file: files/etcd-client.key + etcd_trusted_ca_file: files/client-ca.crt + etcd_peer_cert_file: files/etcd-peer.crt + etcd_peer_key_file: files/etcd-peer.key + etcd_peer_trusted_ca_file: files/etcd-peer-ca.crt + api_cert_file: files/sensu-api.crt + api_key_file: files/sensu-api.key + api_trusted_ca_file: files/sensu-api-ca.crt + dashboard_cert_file: files/sensu-dashboard.crt + dashboard_key_file: files/sensu-dashboard.key + backend_config: + debug: true + log-level: debug + state-dir: /tmp/different-state + etcd-listen-client-urls: "https://127.0.0.1:2379" + etcd-listen-peer-urls: "https://127.0.0.1:2380" + etcd-initial-advertise-peer-urls: "https://127.0.0.1:2380" + etcd-initial-cluster: "default=https://127.0.0.1:2380" + etcd-cert-file: "/etc/sensu/../sensu/etcd-client.crt" + etcd-key-file: "/etc/sensu/../sensu/etcd-client.key" + etcd-trusted-ca-file: "/etc/sensu/../sensu/etcd-client-ca.crt" + etcd-client-cert-auth: false + etcd-peer-cert-file: "/etc/sensu/../sensu/etcd-peer.crt" + etcd-peer-key-file: "/etc/sensu/../sensu/etcd-peer.key" + etcd-peer-client-cert-auth: false + etcd-peer-trusted-ca-file: "/etc/sensu/../sensu/etcd-peer-ca.crt" + cert-file: "/etc/sensu/../sensu/api.crt" + key-file: "/etc/sensu/../sensu/api.key" + trusted-ca-file: "/etc/sensu/../sensu/api-ca.crt" + insecure-skip-tls-verify: true + api-url: "https://127.0.0.1:8080" + dashboard-cert-file: /etc/sensu/../sensu/dashboard.crt + dashboard-key-file: /etc/sensu/../sensu/dashboard.key + + - name: Confirm overriding of managed vars configuration settings + slurp: + src: /etc/sensu/backend.yml + register: backend_yml + + - assert: + that: + - | + backend_yml.content | b64decode | from_yaml == { + "api-url": "https://127.0.0.1:8080", + "cert-file": "/etc/sensu/../sensu/api.crt", + "dashboard-cert-file": "/etc/sensu/../sensu/dashboard.crt", + "dashboard-key-file": "/etc/sensu/../sensu/dashboard.key", + "debug": True, + "etcd-cert-file": "/etc/sensu/../sensu/etcd-client.crt", + "etcd-client-cert-auth": False, + "etcd-initial-advertise-peer-urls": "https://127.0.0.1:2380", + "etcd-initial-cluster": "default=https://127.0.0.1:2380", + "etcd-key-file": "/etc/sensu/../sensu/etcd-client.key", + "etcd-listen-client-urls": "https://127.0.0.1:2379", + "etcd-listen-peer-urls": "https://127.0.0.1:2380", + "etcd-peer-cert-file": "/etc/sensu/../sensu/etcd-peer.crt", + "etcd-peer-client-cert-auth": False, + "etcd-peer-key-file": "/etc/sensu/../sensu/etcd-peer.key", + "etcd-peer-trusted-ca-file": "/etc/sensu/../sensu/etcd-peer-ca.crt", + "etcd-trusted-ca-file": "/etc/sensu/../sensu/etcd-client-ca.crt", + "insecure-skip-tls-verify": True, + "key-file": "/etc/sensu/../sensu/api.key", + "log-level": "debug", + "state-dir": "/tmp/different-state", + "trusted-ca-file": "/etc/sensu/../sensu/api-ca.crt", + } diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt new file mode 100644 index 000000000..fe9c08f48 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y +OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3 +8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS +KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u +53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb +7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg +vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw +gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv +NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB +ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in +NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m +tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K +3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ +RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ +6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt new file mode 100644 index 000000000..85b705566 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 70:e5:70:1d:0c:bb:93:ba:81:72:b6:5b:47:4f:9a:28 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Sensu-test CA + Validity + Not Before: Nov 6 17:22:40 2019 GMT + Not After : Oct 21 17:22:40 2022 GMT + Subject: CN=etcd-client + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:d3:ac:be:a4:45:22:ed:8f:0b:b2:19:0d:ab:60: + db:0b:c4:fb:8a:73:22:9a:f8:54:16:a0:4c:12:00: + a1:21:ef:03:ca:83:29:8e:90:12:87:03:78:12:0e: + 4f:b9:1c:f0:a2:57:06:51:fe:9c:6a:ef:45:9a:6b: + d5:32:df:58:44:ea:9b:ed:4d:e1:19:df:ce:ed:ea: + 90:a4:b1:2e:55:a2:6c:87:53:13:38:de:db:cc:f6: + cc:49:ba:85:aa:29:3d:0e:c9:6a:c0:b8:85:16:a7: + 34:1a:2a:d9:cc:f4:0e:1e:68:35:6d:4a:dd:5d:43: + f6:70:da:0a:27:5e:77:ee:8c:d9:46:f8:70:2e:e3: + 3c:97:7d:55:c7:c6:0d:e7:20:61:df:9e:59:78:97: + 6c:0d:5c:ed:d3:c8:d7:d8:f3:7c:7b:58:2f:0a:f9: + b3:a5:63:41:b6:6a:e6:b2:bc:48:eb:cb:4b:76:2b: + 2e:3e:9c:1a:db:c9:f1:2f:1a:7c:43:db:38:6b:9b: + 7f:d0:d7:25:7c:3f:50:1d:70:11:06:a8:d6:14:b5: + c4:dd:ea:3d:28:d9:57:b7:aa:d2:26:01:cc:5e:f1: + ae:48:28:a1:4a:ee:ac:ed:9c:7e:61:ef:37:12:67: + 7b:d9:05:0b:84:51:75:be:04:96:89:b7:d1:95:d6: + ca:61 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + C5:76:B1:3A:CC:A2:6C:9B:DD:43:D2:59:E1:E2:51:65:A0:59:09:70 + X509v3 Authority Key Identifier: + keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9 + DirName:/CN=Sensu-test CA + serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47 + + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + X509v3 Subject Alternative Name: + IP Address:176.16.117.105 + Signature Algorithm: sha256WithRSAEncryption + 22:8f:57:1e:79:d1:78:9f:d4:fa:c5:41:9d:b1:b0:90:99:d4: + 85:4d:83:0e:ec:c6:0d:fa:a5:4b:7f:16:70:58:9f:5e:1a:f4: + 15:cf:45:2f:cc:69:8e:56:bd:f9:d1:96:6a:34:b5:96:12:b4: + 84:07:21:8a:6f:13:ac:a2:6e:e0:e3:63:17:f3:33:86:ed:c5: + ea:48:c1:b3:af:05:75:28:5f:4f:77:5f:6c:83:cc:50:09:e4: + 56:59:e3:ec:b0:f1:56:01:ca:d0:69:90:b3:e3:a4:a5:c0:cd: + 99:5e:65:ad:33:30:42:53:70:d7:47:c8:4c:47:fd:b6:b1:b1: + f6:cb:0d:8b:6c:ab:14:f5:75:1e:3b:20:f9:00:05:87:bf:e0: + a7:57:12:af:e9:67:9d:5a:96:c3:52:55:25:a8:ec:86:20:49: + 74:e8:6a:19:c0:4a:27:5b:fc:8a:4d:a4:5e:6d:fa:9a:91:48: + c5:e4:84:e1:ed:f9:d5:6e:21:69:6e:20:4a:0e:cb:44:d7:4a: + 38:10:23:12:9f:9b:48:d0:a3:9f:02:e4:84:6d:d9:be:17:eb: + 60:d0:d3:39:60:60:13:7a:b1:a2:38:e4:75:5b:28:4e:5d:25: + 35:04:52:a2:c1:ce:8b:94:9f:37:30:3c:a8:35:36:44:db:ed: + b2:6f:4a:b1 +-----BEGIN CERTIFICATE----- +MIIDbjCCAlagAwIBAgIQcOVwHQy7k7qBcrZbR0+aKDANBgkqhkiG9w0BAQsFADAY +MRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBMB4XDTE5MTEwNjE3MjI0MFoXDTIyMTAy +MTE3MjI0MFowFjEUMBIGA1UEAwwLZXRjZC1jbGllbnQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDTrL6kRSLtjwuyGQ2rYNsLxPuKcyKa+FQWoEwSAKEh +7wPKgymOkBKHA3gSDk+5HPCiVwZR/pxq70Waa9Uy31hE6pvtTeEZ387t6pCksS5V +omyHUxM43tvM9sxJuoWqKT0OyWrAuIUWpzQaKtnM9A4eaDVtSt1dQ/Zw2gonXnfu +jNlG+HAu4zyXfVXHxg3nIGHfnll4l2wNXO3TyNfY83x7WC8K+bOlY0G2auayvEjr +y0t2Ky4+nBrbyfEvGnxD2zhrm3/Q1yV8P1AdcBEGqNYUtcTd6j0o2Ve3qtImAcxe +8a5IKKFK7qztnH5h7zcSZ3vZBQuEUXW+BJaJt9GV1sphAgMBAAGjgbUwgbIwCQYD +VR0TBAIwADAdBgNVHQ4EFgQUxXaxOsyibJvdQ9JZ4eJRZaBZCXAwUwYDVR0jBEww +SoAUZW81fRELC/07z+Y7lfo1a4ANJ+mhHKQaMBgxFjAUBgNVBAMMDVNlbnN1LXRl +c3QgQ0GCFC3obG+lDn1jqLELPYYdLtBiXyFHMBMGA1UdJQQMMAoGCCsGAQUFBwMC +MAsGA1UdDwQEAwIHgDAPBgNVHREECDAGhwSwEHVpMA0GCSqGSIb3DQEBCwUAA4IB +AQAij1ceedF4n9T6xUGdsbCQmdSFTYMO7MYN+qVLfxZwWJ9eGvQVz0UvzGmOVr35 +0ZZqNLWWErSEByGKbxOsom7g42MX8zOG7cXqSMGzrwV1KF9Pd19sg8xQCeRWWePs +sPFWAcrQaZCz46SlwM2ZXmWtMzBCU3DXR8hMR/22sbH2yw2LbKsU9XUeOyD5AAWH +v+CnVxKv6WedWpbDUlUlqOyGIEl06GoZwEonW/yKTaRebfqakUjF5ITh7fnVbiFp +biBKDstE10o4ECMSn5tI0KOfAuSEbdm+F+tg0NM5YGATerGiOOR1WyhOXSU1BFKi +wc6LlJ83MDyoNTZE2+2yb0qx +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key new file mode 100644 index 000000000..1bed38d5c --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTrL6kRSLtjwuy +GQ2rYNsLxPuKcyKa+FQWoEwSAKEh7wPKgymOkBKHA3gSDk+5HPCiVwZR/pxq70Wa +a9Uy31hE6pvtTeEZ387t6pCksS5VomyHUxM43tvM9sxJuoWqKT0OyWrAuIUWpzQa +KtnM9A4eaDVtSt1dQ/Zw2gonXnfujNlG+HAu4zyXfVXHxg3nIGHfnll4l2wNXO3T +yNfY83x7WC8K+bOlY0G2auayvEjry0t2Ky4+nBrbyfEvGnxD2zhrm3/Q1yV8P1Ad +cBEGqNYUtcTd6j0o2Ve3qtImAcxe8a5IKKFK7qztnH5h7zcSZ3vZBQuEUXW+BJaJ +t9GV1sphAgMBAAECggEAX+J1bUYgH0pX+vIcXhB+ySMO6tVizJ5GwQUV0GXWl9+E +cRzfG0QqEFzC7DLtbCYu3ura4xOZ2VrPxbapGeVQP8+imGlZ2XWnb+B8aw4Tthjg +XqCEKZPSL1NwkMlcOQt7LBKTN/+d0fglwuC8TnoTVzTPVtW91rytistKJDFH1JfI +AwOyMoWkNYP3zDdMJn9dWkOXsNjK2VoxzCh/DhOcI/2gO4v7fM1w8A8iGYi5uMD4 +b1IzIqcvdgeyGEXhzu2PcJ0Kr1dR2B2VGJJ4kUqIjUGxG7gSE8X0siEBmSBNBou9 +lFp35esx2ffEnzPZaxAdqOpW0eV/iWxxYKFKX37PAQKBgQDpBOxqgbaGqu20MBVB +rQWh4CjalnvDbjyCyLfAb/6iI8MLnSvw9nfUS9X0TGnMmbZ5gk+7LDKczfs6NOHC +qBs34tAHHebimYqNII5UFc6OyYHpZsLmCG7+JgJDDD299jyZsNHBuw46TUOwdj9T +rRUdAX4s5fDzLmNzu/vN7PqXcQKBgQDojO8qscWy8GWbbxUpRjCetxNPpBf83rke +SL8o283ziMNmhn2L1RHa5vAYBHU1n2aIbYJiU6UEoJnCCpjxcigZdEacE7wsx8Et +alCzlitLuCW0C3bdvxERm1Ya9frhxq+ljE9Kjn8IQ9+1+4zE0Xzk9bnEQVQsySWk +siJUay9J8QKBgQCu6iY5cPM9tZNHfgyGx0WCFM02AF4Y/mfn2Jmul6MxvNyNnEa+ +05RxxRdvEekdT4ldPsdw/iVj9W8Pa2DIiP4dfmGf5f9Ju+34MCcG0XPDVVnyhVPp +7wy6NHfgMpEqRmuJBuT9otd0Rkl4bdrtifBeXJ+FPnoXYYv/9W14T9pv0QKBgBpn +Tgxp7Ml5VAAG578s8f5DSRUEy5hxbVFL7zBjbInXIGB2qrCCu6lACUig0PIKrCiX +TnN9jcHtvidy7pSTYowpUI8OCpKHB57xcEJDrZzGRrmfh5p7xCNcoLUk8pxJ482H +FcWgUjoNAsx1yDDcnDKe1725sSX4nKaLdyxgkxjxAoGAVzuCl7Kl2RjOCIWKG46N +vIl0nCdyTlJb1ammNr2cxIkjreSjenYS+Y8aXJOVVVDux+QB2LiQioMBDN/KIQ0k +55enQr0iKrjNIa5GeGN8uNnsrUoJ/9YfRsEq3rokns7kvSdsNFGFH4UOwuuEd4Qb +y6OFkERhzgGm4TcsRPeOPiM= +-----END PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt new file mode 100644 index 000000000..fe9c08f48 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y +OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3 +8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS +KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u +53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb +7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg +vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw +gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv +NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB +ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in +NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m +tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K +3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ +RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ +6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt new file mode 100644 index 000000000..90e866405 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + ae:53:a2:43:b9:f9:fc:21:b9:f1:2c:e3:c3:b4:45:14 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Sensu-test CA + Validity + Not Before: Nov 6 17:22:40 2019 GMT + Not After : Oct 21 17:22:40 2022 GMT + Subject: CN=etcd-peer + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:dd:2a:35:47:b2:6d:77:0d:d5:a2:16:50:20:97: + 93:e9:f6:c4:01:04:b7:2a:62:04:52:01:68:5c:98: + f2:e5:82:f6:eb:9a:77:9b:ca:0a:9f:61:34:4d:f4: + b5:ae:e0:bd:8a:5d:79:03:b4:10:c3:ea:f3:8c:e2: + 88:79:ff:68:2a:dd:d5:d1:d5:68:9a:61:2d:0b:49: + 03:ae:fc:1b:26:a7:89:1f:fb:76:e2:81:81:72:bd: + 42:c2:9e:60:d9:0d:2e:72:81:c3:11:c6:cd:63:64: + 52:a9:5d:d5:85:c1:35:b0:93:2a:de:d3:46:2c:e4: + 10:d5:7d:92:e9:3c:ff:f5:b4:7e:e2:fc:fd:6b:9e: + c1:a1:a0:c9:6c:31:ad:d2:42:09:55:9e:de:ee:37: + 06:40:c7:61:da:c9:5e:d8:40:cb:3f:1f:7b:93:11: + c3:e5:5c:9d:e5:fc:77:67:92:de:c3:8a:3e:87:15: + dc:47:ad:34:17:84:b4:b2:21:8e:8f:2f:d8:3a:92: + 99:4c:80:d7:06:77:5d:3c:66:db:53:15:ee:a5:5f: + 12:29:19:07:30:a1:71:7b:d5:1f:ee:24:b3:9c:02: + 6e:b6:42:cd:19:96:73:e3:c7:c4:20:c5:a4:75:b4: + b5:85:43:68:e4:91:5f:3d:fa:ed:0e:34:f1:fe:a9: + 0c:a5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 75:26:89:17:1C:ED:2A:53:BD:CE:87:EA:34:F5:C6:76:7C:C3:B5:08 + X509v3 Authority Key Identifier: + keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9 + DirName:/CN=Sensu-test CA + serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47 + + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Subject Alternative Name: + IP Address:176.16.117.105 + Signature Algorithm: sha256WithRSAEncryption + 3a:39:39:8c:d5:9d:ca:88:0e:d3:c4:b2:e5:91:3b:b4:66:5e: + a3:92:44:f4:b4:fa:20:b0:a7:ee:6d:69:f4:8e:51:db:7b:e8: + 50:b9:15:00:2a:20:17:4e:0d:bf:4a:62:56:3e:cf:be:33:f6: + 44:54:c9:5e:22:1a:7a:3f:40:74:77:ed:96:e9:09:9e:27:dd: + 0a:69:bb:d3:5b:b2:4e:95:08:00:71:c1:a6:a2:ed:b9:46:41: + 94:96:6a:10:57:d4:7a:c2:0d:ce:c3:14:0c:ff:ab:fe:6e:09: + fd:18:09:32:d9:7c:f8:a4:dd:0c:fd:56:55:36:ae:03:fc:59: + a3:c5:4b:6f:2e:be:5f:d7:cd:bd:7d:de:3d:29:1a:5b:78:85: + 7a:4c:59:f3:15:31:e6:93:9c:db:28:c8:10:86:6f:30:db:3f: + 59:29:b1:05:9f:9b:b5:2e:20:b2:b8:dc:2e:3b:c2:75:dc:bf: + 39:eb:db:d6:5c:f7:cc:b5:61:ea:a8:c0:45:89:5b:c2:1a:fa: + ce:a7:69:63:4b:f6:3c:be:4c:f2:cf:2b:bf:5b:d4:f1:b8:e3: + 2a:e2:50:3a:62:e5:0d:42:fc:ac:63:b2:76:3d:c4:0a:26:70: + 60:16:ec:12:d7:60:04:10:bd:e7:9d:7c:3c:65:ab:25:51:f7: + aa:6b:5e:b1 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIRAK5TokO5+fwhufEs48O0RRQwDQYJKoZIhvcNAQELBQAw +GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDBaFw0yMjEw +MjExNzIyNDBaMBQxEjAQBgNVBAMMCWV0Y2QtcGVlcjCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAN0qNUeybXcN1aIWUCCXk+n2xAEEtypiBFIBaFyY8uWC +9uuad5vKCp9hNE30ta7gvYpdeQO0EMPq84ziiHn/aCrd1dHVaJphLQtJA678Gyan +iR/7duKBgXK9QsKeYNkNLnKBwxHGzWNkUqld1YXBNbCTKt7TRizkENV9kuk8//W0 +fuL8/WuewaGgyWwxrdJCCVWe3u43BkDHYdrJXthAyz8fe5MRw+VcneX8d2eS3sOK +PocV3EetNBeEtLIhjo8v2DqSmUyA1wZ3XTxm21MV7qVfEikZBzChcXvVH+4ks5wC +brZCzRmWc+PHxCDFpHW0tYVDaOSRXz367Q408f6pDKUCAwEAAaOBtTCBsjAJBgNV +HRMEAjAAMB0GA1UdDgQWBBR1JokXHO0qU73Oh+o09cZ2fMO1CDBTBgNVHSMETDBK +gBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vuc3UtdGVz +dCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYBBQUHAwEw +CwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQELBQADggEB +ADo5OYzVncqIDtPEsuWRO7RmXqOSRPS0+iCwp+5tafSOUdt76FC5FQAqIBdODb9K +YlY+z74z9kRUyV4iGno/QHR37ZbpCZ4n3Qppu9Nbsk6VCABxwaai7blGQZSWahBX +1HrCDc7DFAz/q/5uCf0YCTLZfPik3Qz9VlU2rgP8WaPFS28uvl/Xzb193j0pGlt4 +hXpMWfMVMeaTnNsoyBCGbzDbP1kpsQWfm7UuILK43C47wnXcvznr29Zc98y1Yeqo +wEWJW8Ia+s6naWNL9jy+TPLPK79b1PG44yriUDpi5Q1C/KxjsnY9xAomcGAW7BLX +YAQQveedfDxlqyVR96prXrE= +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key new file mode 100644 index 000000000..3d3dfd47b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdKjVHsm13DdWi +FlAgl5Pp9sQBBLcqYgRSAWhcmPLlgvbrmnebygqfYTRN9LWu4L2KXXkDtBDD6vOM +4oh5/2gq3dXR1WiaYS0LSQOu/Bsmp4kf+3bigYFyvULCnmDZDS5ygcMRxs1jZFKp +XdWFwTWwkyre00Ys5BDVfZLpPP/1tH7i/P1rnsGhoMlsMa3SQglVnt7uNwZAx2Ha +yV7YQMs/H3uTEcPlXJ3l/Hdnkt7Dij6HFdxHrTQXhLSyIY6PL9g6kplMgNcGd108 +ZttTFe6lXxIpGQcwoXF71R/uJLOcAm62Qs0ZlnPjx8QgxaR1tLWFQ2jkkV89+u0O +NPH+qQylAgMBAAECggEALk6aHUr0tIrHAksdt9VE+SXb4EK2fz9KnEkDKvAzW27S +eH49MPdaxgg6RWBJcjZIOWJc5jOblwnouMTtwm2ByAfuryK55ikWn1hIVykeHjfR +9EpYmBB5pCaQheNXb9rcsMkOqPgxJPBqhl/JR1ou/auyvDkMZnXSOIE0c3V8OluO +nL39RHNCz76bNZSpe6BbYH6Ch8xkhCuYrzlIuBTSduE1aOK/Zugf+AMlNbxekdYv +8rhlCkO4lX752Dy8Z6x6KqRwRrXk+yeAhL7BITpsMCtJCR7XIsXKr9u/465tkjhC +DlkqFhwk2I9uG0ygtkUkYpfakx0FBoroAQc3qAgQuQKBgQD+6BPb9nvvS403DcEh +KNV0Pegdjms4KGiZmhOskWPOmMVKyIUHULgcQiwoROPzSfHjHABRuaVUz2+n37Kx +kU9h+FTolIQUD/S9aYwEIf83Eu7CGj9ictLUPeLxbGSJ6B5+74D8RuOj5jS2VNug +kJeBl0OWgD7ZXJF3LotSmnyiuwKBgQDeHRPaaBqp+5rZOLghyztsXnXQEcvMl5pt +wUnm77XMfDIJf+KDKdOA7avelcMsuv1UDQNpckshaeU6W5UYrw6CI/gpB5JHJBjQ +Bs6kTEw73t0+B1C9X/MA/AHIbk4TYLluxrv8pCprAptACuvEyHX+d03lETw+slzD +mLWDMnqIHwKBgQCDbifnL1TBkkPyke31afd9IvpOWwNfhj5AjJf+T0yV1mFLaM5m +cjErqNbZwIOECqlkfyiO+tiLPRWGCio5sgGrMv6cmQ7sxTlcfFJMQzczL1jZzezG +lwurkWk1L706+erXaigoa2iuNmERbfl79XGYyOR5chB1xGvgdqgxZCRFRwKBgDxz +s4yVGvS6uwl2C39/Hdw/1VbdERfNB0Xp/qAxC8zs3H2DZfYG8z668TUyk51gA0TW +CeCwL8yXUsFQXcMLGirHeWpJWkGsjGhKAgHrljARVyvjt9DjBXN2I1IW238gqzeA +NXfsgnL/kZubnVHAsYShmfzHdRsnVmIR9Q0RNxJHAoGBAMfqM6egc5JRSmDhA408 +d/oKasqYuPfNKWjZNfmDJcs1yHjaCJdQMkLlAi7a9QTi05KfEJGFf/k+ai7OIInw +TCgworaS8+y/1YihDIMGHfFF2B3IYEJQkEIDHXUBaVA9xMXgYLBDYAme82riZia2 +9mDJn1VPU0HRotyDYwoC34JV +-----END PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt new file mode 100644 index 000000000..fe9c08f48 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y +OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3 +8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS +KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u +53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb +7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg +vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw +gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv +NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB +ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in +NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m +tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K +3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ +RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ +6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w== +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt new file mode 100644 index 000000000..68363428f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + a0:13:00:67:13:31:d4:25:47:fa:1a:48:2b:76:a9:7f + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Sensu-test CA + Validity + Not Before: Nov 6 17:22:42 2019 GMT + Not After : Oct 21 17:22:42 2022 GMT + Subject: CN=sensu-api + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:c4:16:71:d3:3b:08:26:1e:81:60:96:6e:9c:75: + ec:58:f2:dc:8f:22:e7:c8:64:b9:52:55:11:51:d5: + eb:d7:ae:7f:48:81:25:ea:4e:66:33:ee:be:73:25: + 84:ed:02:a6:f5:5f:d4:c0:2b:a6:d3:ae:c7:41:64: + 2c:5e:4b:6c:12:92:64:53:41:4a:59:b6:ef:d2:88: + 24:96:26:30:e3:98:25:45:c6:59:c4:e6:33:2c:a2: + 38:25:ce:f5:d5:d2:ba:39:68:1b:ec:f1:77:e6:e1: + 20:19:40:e5:ed:8c:bc:44:40:ce:0c:26:7d:86:60: + 0d:19:1f:31:59:85:a5:6a:d2:41:c8:1c:d6:7e:7b: + d1:1f:d4:35:2e:c2:c8:f6:da:03:39:54:0e:e6:8c: + 1b:26:e4:e6:3c:1f:88:64:c7:4f:46:89:70:6c:5b: + 26:ee:f2:a3:26:93:ac:00:4f:5d:d3:8b:5f:19:cc: + 53:f8:68:6b:61:fe:20:ba:b5:af:8a:9c:04:ea:bd: + 25:2c:e5:bf:7d:89:f3:d0:8d:90:79:7e:62:0e:88: + 47:cc:cf:94:26:00:76:c9:c1:93:85:a5:1b:19:5e: + 34:73:3a:eb:23:72:23:03:d6:83:29:26:4c:5d:4f: + ad:7d:1c:1f:e7:85:c3:d4:4e:0a:b2:1f:68:c4:67: + 19:c3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + E8:97:A6:14:2C:C4:75:73:3A:9B:AD:8D:91:3E:1D:6C:7E:C0:C1:9D + X509v3 Authority Key Identifier: + keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9 + DirName:/CN=Sensu-test CA + serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47 + + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Subject Alternative Name: + IP Address:176.16.117.105 + Signature Algorithm: sha256WithRSAEncryption + 13:92:90:3f:2e:08:d3:e7:ce:77:4d:fe:db:d0:75:ed:4f:ca: + ab:6e:2a:8c:6d:05:1d:b2:d0:be:2f:ab:f5:ff:84:71:e0:24: + 93:92:89:2a:e4:14:ef:1c:dc:d3:e7:f1:6e:44:cc:35:b9:90: + 9b:c4:c3:18:64:97:bb:70:67:22:59:94:cd:c0:f3:f2:85:7c: + f6:d0:97:e2:f5:4a:fa:6a:0a:94:19:b3:de:90:84:1c:49:12: + 9c:f7:29:93:51:07:9f:84:61:36:19:86:02:6e:7f:8f:1c:e2: + 86:93:8b:cb:04:f4:54:b6:9b:22:ee:c3:bb:ab:6f:7d:f8:ae: + 9e:6b:71:e1:f7:4b:73:21:a2:f4:d3:44:1e:20:f5:55:10:9b: + ce:24:33:24:5f:21:a3:d3:75:c3:76:0d:b5:35:b3:95:d9:da: + 97:ed:15:9e:3e:6e:df:0f:a3:2a:10:e4:f9:49:b8:b2:f2:b2: + 99:e3:99:12:88:20:5c:2f:ad:1a:d6:e7:f1:29:e4:4d:e3:fb: + 30:72:1a:1b:53:69:ac:40:f7:a5:84:8d:12:e9:6f:78:a6:b9: + c0:b5:28:28:64:88:c9:da:69:d0:8e:6b:e9:7c:56:9f:68:ad: + 09:76:e0:a3:26:21:86:65:ff:7b:15:89:12:41:83:52:04:5d: + 61:b4:88:25 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIRAKATAGcTMdQlR/oaSCt2qX8wDQYJKoZIhvcNAQELBQAw +GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDJaFw0yMjEw +MjExNzIyNDJaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMQWcdM7CCYegWCWbpx17Fjy3I8i58hkuVJVEVHV69eu +f0iBJepOZjPuvnMlhO0CpvVf1MArptOux0FkLF5LbBKSZFNBSlm279KIJJYmMOOY +JUXGWcTmMyyiOCXO9dXSujloG+zxd+bhIBlA5e2MvERAzgwmfYZgDRkfMVmFpWrS +Qcgc1n570R/UNS7CyPbaAzlUDuaMGybk5jwfiGTHT0aJcGxbJu7yoyaTrABPXdOL +XxnMU/hoa2H+ILq1r4qcBOq9JSzlv32J89CNkHl+Yg6IR8zPlCYAdsnBk4WlGxle +NHM66yNyIwPWgykmTF1PrX0cH+eFw9ROCrIfaMRnGcMCAwEAAaOBtTCBsjAJBgNV +HRMEAjAAMB0GA1UdDgQWBBTol6YULMR1czqbrY2RPh1sfsDBnTBTBgNVHSMETDBK +gBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vuc3UtdGVz +dCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYBBQUHAwEw +CwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQELBQADggEB +ABOSkD8uCNPnzndN/tvQde1PyqtuKoxtBR2y0L4vq/X/hHHgJJOSiSrkFO8c3NPn +8W5EzDW5kJvEwxhkl7twZyJZlM3A8/KFfPbQl+L1SvpqCpQZs96QhBxJEpz3KZNR +B5+EYTYZhgJuf48c4oaTi8sE9FS2myLuw7urb334rp5rceH3S3MhovTTRB4g9VUQ +m84kMyRfIaPTdcN2DbU1s5XZ2pftFZ4+bt8PoyoQ5PlJuLLyspnjmRKIIFwvrRrW +5/Ep5E3j+zByGhtTaaxA96WEjRLpb3imucC1KChkiMnaadCOa+l8Vp9orQl24KMm +IYZl/3sViRJBg1IEXWG0iCU= +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key new file mode 100644 index 000000000..e5854027d --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEFnHTOwgmHoFg +lm6cdexY8tyPIufIZLlSVRFR1evXrn9IgSXqTmYz7r5zJYTtAqb1X9TAK6bTrsdB +ZCxeS2wSkmRTQUpZtu/SiCSWJjDjmCVFxlnE5jMsojglzvXV0ro5aBvs8Xfm4SAZ +QOXtjLxEQM4MJn2GYA0ZHzFZhaVq0kHIHNZ+e9Ef1DUuwsj22gM5VA7mjBsm5OY8 +H4hkx09GiXBsWybu8qMmk6wAT13Ti18ZzFP4aGth/iC6ta+KnATqvSUs5b99ifPQ +jZB5fmIOiEfMz5QmAHbJwZOFpRsZXjRzOusjciMD1oMpJkxdT619HB/nhcPUTgqy +H2jEZxnDAgMBAAECggEAK8zTqhpCjLk9rwSLOpnArHG7QKHMYl/VYYWs87m0D55j +wh9PB9JxU+JdWj0kPwjboG3CiRZ3Eku1KG8m1f1E67UVgd9Qq0+IrF9KxNtNClme +4cIXpTrCbZLitddP5G5IuK//pOKfJMxeriVn9rL8Dsbm/6HNYimsY1MrY9LNi1l9 +zAxSAihUPI0JM0DEA+5C4vaIFiOt1ulhTXuRU94v8gjcDMnpp/rB8pP0XvpjNdzb +7LExeMrywEOnxGEju1Co5Q5jp2epDwjSowlTg+wJCu0dlCW6uWGV0umW+KwK+aQ0 +Z/Mo4VwE4kRO/2vth9N0tboHOL5EoDdKprk1v5E1+QKBgQDi8nqgaDlfsO4HIGPE +NiQvevxQBQ7D2vElt04fYpla4iTJpmXeo35194sW40pAbt2N3/xsEAFZPGWiGWVe +km8tLdF2vHHNp4wFHEjb86hu6h07XAikr+1+ZI+musn09lgH8gXmy610PJ+OLHnf +Rmkfhr73EhBa2PLp4GArwq6D/wKBgQDdMKL/SkdwCUqz3Z3UP2cRBBNi0JDXCGTb +jpQMvJZtrosxR9su55w5ZtIKLRPn7fR0AfRztBKUQBmEYgcp+1VkhIA5v6GGzNAj +af/dVZhyXV9OPdSc5pDk+bWbrj8GxQr/5C04clVnwZ2ESNm8tulpwo2MZPPoj/vk +tuN7UZZaPQKBgQCAZLhVicFzzrBLXdqzhgHgzs5yIvpgebxWHydWgDzMewZfAwG8 +/HguGzcYYsx+OXqkqmSvajqpFo9VLtL3txao07QeXaxwsep4dbEOpwHShia1j3Lg +YRuWlyPiKujY6omRLS6DjRV7nlSSZb0pQTd1+5CMTS7thrGe+S7PcxuyVwKBgBgK +WDztAtSvfdoMxUGzXm1gBwdfac6lT+j5FyhHOwZSyTgi+jSf4b/vZ/bJLXewyjft +mncU5EwOp3dW/DZY5dAWAqXEKTcwfZLLy45v5jDP6zLiz3/6I1dvuIhiKOGAexCS +6UNQUe4EAi3FiTzUmIvxJFdVBZmKRLN4GUNm+7N1AoGAA9VfdyWQYTu+z0y91/ob +y77j0hiG+A7HK7PNW2ZIwnhjczxuQ7Dcpk/GIqnOamrslLMNRU9zNETPRdEkoMnX +I8ZyrGFKCby9FNBHpgbDFjY7+EgaoWwVjt4UhbZOQQAVVkoBS3CUSMFxs29iAun1 +uqY0wSO5KtVggDqqcZJpkHw= +-----END PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt new file mode 100644 index 000000000..8cc61dada --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt @@ -0,0 +1,87 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 9a:76:0c:19:84:14:e0:bf:39:06:f1:25:83:09:32:fc + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Sensu-test CA + Validity + Not Before: Nov 6 17:22:42 2019 GMT + Not After : Oct 21 17:22:42 2022 GMT + Subject: CN=sensu-dashboard + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:b0:e0:10:ec:e6:50:88:db:f6:78:ba:21:a4:c4: + 17:b5:f0:57:9b:54:da:4d:1f:75:d2:da:15:4f:17: + 1f:d7:cd:15:0e:1e:39:7c:50:6a:32:11:11:9c:76: + 01:77:8e:a4:07:49:55:49:8e:96:1a:c7:a5:90:3e: + 69:4d:ec:a9:45:a3:07:05:89:7a:dd:a6:34:cb:29: + c8:36:71:ba:b6:ec:d8:20:60:58:5f:23:ca:61:78: + e1:ca:a5:6a:b6:ea:d9:dd:4f:ce:fb:2e:9c:9d:06: + eb:de:d9:be:58:d3:a8:da:2b:2d:4f:f9:7a:32:43: + 52:68:60:41:e5:b7:65:c7:4d:ec:ae:56:99:b1:05: + 66:5c:77:03:41:28:e1:b9:75:7f:67:3c:09:e7:d3: + 4d:f2:77:51:74:2c:8b:82:ab:5f:97:ea:c0:f7:45: + e5:85:35:a2:5c:95:65:79:a1:dd:bd:bf:a6:e8:76: + ae:36:c7:36:7c:e7:d9:e8:57:65:e2:3f:ea:0b:8d: + 16:61:3c:c9:3b:89:3d:a7:cf:5a:09:4c:f2:98:03: + fd:6d:d0:20:8c:12:4c:a3:38:af:53:71:75:54:30: + f9:e8:d3:44:4e:f2:fc:b8:ec:f9:49:ff:c8:bc:27: + 00:b2:2c:c6:15:05:b4:5b:f7:ac:20:fc:df:b5:56: + 26:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Key Identifier: + 08:7A:07:19:64:FD:EA:87:C3:25:FE:65:E6:8B:A7:9E:E2:46:8C:A7 + X509v3 Authority Key Identifier: + keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9 + DirName:/CN=Sensu-test CA + serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47 + + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Subject Alternative Name: + IP Address:176.16.117.105 + Signature Algorithm: sha256WithRSAEncryption + 47:ca:52:10:7a:01:5e:e3:0d:ae:e7:5e:a7:f8:19:d2:0e:0a: + 03:ef:9f:dc:2f:4f:94:05:15:ae:ee:66:6a:7c:41:cc:07:8f: + 75:09:8c:be:e6:ea:8c:1c:f2:d5:20:23:76:b1:cb:ec:da:1e: + 07:83:23:3b:ce:62:07:65:1f:49:26:f5:59:50:d0:2b:b2:5f: + 61:9d:6d:6b:1f:e4:8d:60:ae:c2:fd:02:40:38:4f:6c:d3:48: + 5c:ca:4e:ac:70:88:63:57:21:f8:47:57:be:d9:bb:ec:ce:e4: + ac:be:81:f7:21:60:95:6d:56:b9:ce:93:8b:f5:3f:c9:de:77: + 3b:34:9a:73:d1:9a:99:ea:35:47:ef:fb:01:48:98:6d:f9:dd: + d2:16:7e:f2:d5:17:d0:66:c7:b1:2b:55:18:80:f7:7d:5d:d3: + 1b:11:18:ef:1d:e4:35:a5:7d:25:a8:b3:cd:80:5c:7e:95:4d: + 9b:79:e1:b0:38:84:75:58:f1:45:4f:48:ed:8b:84:9b:a6:26: + a0:f6:d1:f0:f7:f2:1c:e7:47:ee:b3:30:a9:61:82:4b:fc:d9: + dc:45:ab:2c:f4:30:60:19:0e:92:cc:67:d8:3b:6e:26:80:fb: + b3:3c:21:4e:66:45:fe:05:55:6a:88:04:74:85:ca:1a:75:70: + 38:0f:ca:d4 +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgIRAJp2DBmEFOC/OQbxJYMJMvwwDQYJKoZIhvcNAQELBQAw +GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDJaFw0yMjEw +MjExNzIyNDJaMBoxGDAWBgNVBAMMD3NlbnN1LWRhc2hib2FyZDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALDgEOzmUIjb9ni6IaTEF7XwV5tU2k0fddLa +FU8XH9fNFQ4eOXxQajIREZx2AXeOpAdJVUmOlhrHpZA+aU3sqUWjBwWJet2mNMsp +yDZxurbs2CBgWF8jymF44cqlarbq2d1PzvsunJ0G697ZvljTqNorLU/5ejJDUmhg +QeW3ZcdN7K5WmbEFZlx3A0Eo4bl1f2c8CefTTfJ3UXQsi4KrX5fqwPdF5YU1olyV +ZXmh3b2/puh2rjbHNnzn2ehXZeI/6guNFmE8yTuJPafPWglM8pgD/W3QIIwSTKM4 +r1NxdVQw+ejTRE7y/Ljs+Un/yLwnALIsxhUFtFv3rCD837VWJuMCAwEAAaOBtTCB +sjAJBgNVHRMEAjAAMB0GA1UdDgQWBBQIegcZZP3qh8Ml/mXmi6ee4kaMpzBTBgNV +HSMETDBKgBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vu +c3UtdGVzdCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYB +BQUHAwEwCwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQEL +BQADggEBAEfKUhB6AV7jDa7nXqf4GdIOCgPvn9wvT5QFFa7uZmp8QcwHj3UJjL7m +6owc8tUgI3axy+zaHgeDIzvOYgdlH0km9VlQ0CuyX2GdbWsf5I1grsL9AkA4T2zT +SFzKTqxwiGNXIfhHV77Zu+zO5Ky+gfchYJVtVrnOk4v1P8nedzs0mnPRmpnqNUfv ++wFImG353dIWfvLVF9Bmx7ErVRiA931d0xsRGO8d5DWlfSWos82AXH6VTZt54bA4 +hHVY8UVPSO2LhJumJqD20fD38hznR+6zMKlhgkv82dxFqyz0MGAZDpLMZ9g7biaA ++7M8IU5mRf4FVWqIBHSFyhp1cDgPytQ= +-----END CERTIFICATE----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key new file mode 100644 index 000000000..da9a9f952 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCw4BDs5lCI2/Z4 +uiGkxBe18FebVNpNH3XS2hVPFx/XzRUOHjl8UGoyERGcdgF3jqQHSVVJjpYax6WQ +PmlN7KlFowcFiXrdpjTLKcg2cbq27NggYFhfI8pheOHKpWq26tndT877LpydBuve +2b5Y06jaKy1P+XoyQ1JoYEHlt2XHTeyuVpmxBWZcdwNBKOG5dX9nPAnn003yd1F0 +LIuCq1+X6sD3ReWFNaJclWV5od29v6bodq42xzZ859noV2XiP+oLjRZhPMk7iT2n +z1oJTPKYA/1t0CCMEkyjOK9TcXVUMPno00RO8vy47PlJ/8i8JwCyLMYVBbRb96wg +/N+1VibjAgMBAAECggEBAKrHTdBO+MeMCsi6fy2FoJcs/omePkFk9PCnXRfGbhqB +i6jcCgk7461/yY9WaUg006+tYMnrAIfO6M8DU83HihEQvgDco2NLzYG0T/oxiWSB +/pQUMn96IThH7UsquITw8Xa8Tk88zD7ZpfjAKQe/JjOwqMmlShUp53GcL0RL9due +huI8QBzbiKvBUMME1A8yaNbyy6BwHBjxfJHXaSeNwmUnlKS0A/jMNKtav8jCfdnt +QEuBdL4kexZf/lt+Mu6PmODoeT8VZRrxEf4v79MZxJZNTLOVUjIhELQF1UESkbqI +RoU1vVzU7CsbOWHAaym7ShZK3QDzfnVLkhFAEwsRPsECgYEA6n+SULpYTR7CtBlw +zVSPePe8CiA2W28VoQrXk/9FY/j1o2cGyHidl0mVuagIROW+LyCv+HQxinJzSQrK +AW/aoPgrUUfkTbpmxrPvM5FszG5cVkdFYMsW2Ni/CY8LCRdUReICdEl5xDMMfB2e +/yM6oNFh7LLNrSwUsGlifJ7OcPMCgYEAwRflbOzB98lV6guQJRQISqO4+9OCIXJL +ZDoHxaJ9TVr3cEFUYfm7gFvtgn2QFlqgfCN1omY3vlTuYp4llFw/Z4NX9F673erU +jtt26C1pxmC7kdGApupz+gVWSqO4WhOSgpEUErUX9aX+YEy6UmdzkHnb/1cyT1eG +lVvz2/eUblECgYA9aw5agIQSJuVeIG+wB97QEyq4CDnUduLWXC2cgLae+Zz0oE5h +gV3dOxOxHbaUvQuz8j7Et0ImfdV+IwpHmBFOKdHGpyq/xPuYPZaADi3N2XXrzxz3 +vhmM0DAxA7sjNW4II6r65Ce1YJ17gJKdRo/bgRvB0A8YtTvx/JgkBcASSwKBgBzx +2BJb6zeZlqde1Fy6hAOsRy54pikdWO/NQxz9HotZ9318TYniRZkYLqJA8DhpnWT+ +a8PMTs7ZLGLcEgYLTfXWWnjnOoIpkXNYsppbNF/oYDWbkg1zV69C3YySvi/Cf1PT +K48iVlUcbOVCmyt/FnOx0KiWCZSbKjF5dzSiCD4BAoGAeWRARbEkptVmG1iBdhU1 +SNy1WhNU2L6TfUEbevYWWElQYZiQmwR4XvOfRaR3Dx8pTicABqcAwLgYPbqzCknu +QcPULpLU/EJKP9xRhZClJVSJZ7JRHQrZt/i0WRVuTCCtafp50dns0vMYWBF4vVrE +KUa5kMVzh3unaWHUCJ7qODw= +-----END PRIVATE KEY----- diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml new file mode 100644 index 000000000..9d526ef36 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml @@ -0,0 +1,18 @@ +--- +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - destroy + +platforms: + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true + override_command: false + privileged: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml new file mode 100644 index 000000000..40ba62238 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml @@ -0,0 +1,24 @@ +--- +- name: Prepare + hosts: all + + tasks: + - name: Create sensu group + group: + name: sensu + + - name: Create sensu user + # We need FQCN here because we are running test from within the + # collection. In this case, our collection becomes the default + # collection and so the sensu.sensu_go.user module shadows the builtin + # one. + ansible.builtin.user: + name: sensu + groups: sensu + + - name: Create /etc/sensu folder + file: + state: directory + path: /etc/sensu + owner: sensu + group: sensu diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml new file mode 100644 index 000000000..0aa2bf12a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml @@ -0,0 +1,50 @@ +--- +- name: Test different version builds + hosts: all + tasks: + - name: Install a specific build + include_role: + name: sensu.sensu_go.install + vars: + components: [sensu-go-backend] + channel: testing + version: 5.16.0 + build: 8290 + + - package_facts: + manager: auto + + - assert: + that: + - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0-8290" + when: ansible_facts.packages["sensu-go-backend"][0].source == "apt" + + - assert: + that: + - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0" + - ansible_facts.packages["sensu-go-backend"][0].release == "8290" + when: ansible_facts.packages["sensu-go-backend"][0].source == "rpm" + + - name: Update to a specific build + include_role: + name: sensu.sensu_go.install + tasks_from: packages + vars: + components: [sensu-go-backend] + channel: testing + version: 5.16.0 + build: 8320 + + - package_facts: + manager: auto + + - assert: + that: + - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0-8320" + when: ansible_facts.packages["sensu-go-backend"][0].source == "apt" + + - assert: + that: + - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0" + - ansible_facts.packages["sensu-go-backend"][0].release == "8320" + when: ansible_facts.packages["sensu-go-backend"][0].source == "rpm" diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml new file mode 100644 index 000000000..59e497b6b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml @@ -0,0 +1,11 @@ +--- +platforms: + - name: ubuntu + image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04 + pre_build_image: true + pull: true + + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml new file mode 100644 index 000000000..70be5856e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml @@ -0,0 +1,31 @@ +--- +- name: Converge with older versions of components + hosts: all + + tasks: + - name: Install all components + include_role: + name: sensu.sensu_go.install + vars: + components: + - sensu-go-backend + - sensu-go-agent + - sensu-go-cli + version: 5.14.1 + + - name: Make sure components are installed + command: + cmd: "{{ item }} version" + loop: + - sensu-backend + - sensu-agent + - sensuctl + register: result + + - assert: + quiet: true + that: + - item.stdout is search("5.14.1") + loop: "{{ result.results }}" + loop_control: + label: "{{ item.item }}" # Reduce verbosity a bit diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml new file mode 100644 index 000000000..59e497b6b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml @@ -0,0 +1,11 @@ +--- +platforms: + - name: ubuntu + image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04 + pre_build_image: true + pull: true + + - name: centos + image: quay.io/xlab-steampunk/sensu-go-tests-centos:7 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml new file mode 100644 index 000000000..7ac2dd838 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml @@ -0,0 +1,13 @@ +--- +- name: Converge with latest versions of components + hosts: all + + tasks: + - name: Install all components + include_role: + name: sensu.sensu_go.install + vars: + components: + - sensu-go-backend + - sensu-go-agent + - sensu-go-cli diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml new file mode 100644 index 000000000..1261bc9d0 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml @@ -0,0 +1,35 @@ +--- +scenario: + test_sequence: + - destroy + - create + - converge + - verify + - check + - destroy + +platforms: + - name: debian-9 + image: quay.io/xlab-steampunk/sensu-go-tests-debian:9 + pre_build_image: true + pull: true + + - name: debian-10 + image: quay.io/xlab-steampunk/sensu-go-tests-debian:10 + pre_build_image: true + pull: true + + - name: ubuntu-14.04 + image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:14.04 + pre_build_image: true + pull: true + + - name: ubuntu-16.04 + image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04 + pre_build_image: true + pull: true + + - name: ubuntu-18.04 + image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:18.04 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml new file mode 100644 index 000000000..8dadfc5ef --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml @@ -0,0 +1,12 @@ +--- +- name: Verify + hosts: all + + tasks: + - name: Make sure components are installed + command: + cmd: "{{ item }} version" + loop: + - sensu-backend + - sensu-agent + - sensuctl diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml new file mode 100644 index 000000000..7ac2dd838 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml @@ -0,0 +1,13 @@ +--- +- name: Converge with latest versions of components + hosts: all + + tasks: + - name: Install all components + include_role: + name: sensu.sensu_go.install + vars: + components: + - sensu-go-backend + - sensu-go-agent + - sensu-go-cli diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml new file mode 100644 index 000000000..a0d8b5e6c --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml @@ -0,0 +1,30 @@ +--- +scenario: + test_sequence: + - destroy + - create + - converge + - verify + - check + - destroy + +platforms: + - name: redhat-7 + image: quay.io/xlab-steampunk/sensu-go-tests-redhat:7 + pre_build_image: true + pull: true + + - name: amazon-1 + image: quay.io/xlab-steampunk/sensu-go-tests-amazon:1 + pre_build_image: true + pull: true + + - name: amazon-2 + image: quay.io/xlab-steampunk/sensu-go-tests-amazon:2 + pre_build_image: true + pull: true + + - name: oracle-8 + image: quay.io/xlab-steampunk/sensu-go-tests-oracle:8 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml new file mode 100644 index 000000000..8dadfc5ef --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml @@ -0,0 +1,12 @@ +--- +- name: Verify + hosts: all + + tasks: + - name: Make sure components are installed + command: + cmd: "{{ item }} version" + loop: + - sensu-backend + - sensu-agent + - sensuctl diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml new file mode 100644 index 000000000..b498ee945 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml @@ -0,0 +1,39 @@ +--- +- name: Test downgrade + hosts: all + + tasks: + - name: Install initial version components + include_role: + name: sensu.sensu_go.install + vars: + components: + - sensu-go-agent + version: 6.2.5 + + - name: Make sure components are installed + command: + cmd: sensu-agent version + register: result + - assert: + # quiet: true + that: + - result.stdout is search("6.2.5") + + - name: Downgrade components + include_role: + name: sensu.sensu_go.install + tasks_from: packages + vars: + components: + - sensu-go-agent + version: 6.1.4 + + - name: Make sure components were downgraded + command: + cmd: sensu-agent version + register: result + - assert: + quiet: true + that: + - result.stdout is search("6.1.4") diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml new file mode 100644 index 000000000..c276ce556 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml @@ -0,0 +1,13 @@ +--- +platforms: + # yum test + - name: redhat-7 + image: quay.io/xlab-steampunk/sensu-go-tests-redhat:7 + pre_build_image: true + pull: true + + # apt test + - name: debian-10 + image: quay.io/xlab-steampunk/sensu-go-tests-debian:10 + pre_build_image: true + pull: true diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt new file mode 100644 index 000000000..af9fcfb5e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt @@ -0,0 +1,4 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt new file mode 100644 index 000000000..5a7ce7659 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt @@ -0,0 +1,3 @@ +plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module +tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints +tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py b/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py new file mode 100755 index 000000000..b8544ff9e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import argparse +import json +import os +import sys + +import yaml + +import urllib.request + + +def _get_arg_parser(): + parser = argparse.ArgumentParser(description="Validate role metadata") + parser.add_argument("role", nargs="+", help="role path") + return parser + + +def _validate_role_platforms(platforms): + base_url = "https://galaxy.ansible.com/api/v1/platforms/?name={0}&release={1}" + msgs = [] + for platform in platforms: + for release in platform["versions"]: + url = base_url.format(platform["name"], release) + f = urllib.request.urlopen(url) + + if len(json.loads(f.read().decode('utf-8'))["results"]) != 1: + msgs.append(("ERROR", "Invalid platform '{0} {1}'".format( + platform["name"], release, + ))) + + return msgs + + +def _validate_role(role_path): + meta_file = os.path.join(role_path, "meta", "main.yml") + with open(meta_file) as fd: + galaxy_info = yaml.safe_load(fd)["galaxy_info"] + + msgs = [] + msgs.extend(_validate_role_platforms(galaxy_info["platforms"])) + + return msgs + + +def main(): + args = _get_arg_parser().parse_args() + no_msgs = 0 + for role in args.role: + msgs = _validate_role(role) + for msg in msgs: + no_msgs += 1 + print("{0}: {1}".format(*msg)) + + return 0 if no_msgs == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py new file mode 100644 index 000000000..100b30c6b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py @@ -0,0 +1,316 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible.playbook.task import Task + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + bonsai, errors, +) +from ansible_collections.sensu.sensu_go.plugins.action import bonsai_asset + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestValidate: + @pytest.mark.parametrize("name,args,required,typ", [ + # Required values must match the selected type. + ("a", dict(a=3), True, int), + ("a", dict(a=3.3), True, float), + ("a", dict(a="b"), True, str), + ("a", dict(a=[]), True, list), + ("a", dict(a={}), True, dict), + # Optional values are not checked for type-correctness if they are + # missing. + ("a", dict(), False, int), + ("a", dict(), False, float), + ("a", dict(), False, str), + ("a", dict(), False, list), + ("a", dict(), False, dict), + ]) + def test_valid_values(self, name, args, required, typ): + bonsai_asset.validate(name, args, required, typ) + + def test_missing_required(self): + with pytest.raises(errors.Error, match="required"): + bonsai_asset.validate("a", {}, True, str) + + def test_invalid_type(self): + with pytest.raises(errors.Error, match="should"): + bonsai_asset.validate("a", dict(a=3), True, str) + + def test_invalid_type_for_optional_value(self): + with pytest.raises(errors.Error, match="should"): + bonsai_asset.validate("a", dict(a=3), False, dict) + + +class TestValidateArguments: + def test_valid_minimal_args(self): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version="1.2.3", + )) + + def test_valid_all_args(self): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version="1.2.3", rename="def", + labels={}, annotations={}, + )) + + def test_valid_unicode_strings_python2(self): + bonsai_asset.ActionModule.validate_arguments(dict( + name=u"abc", version=u"1.2.3", rename=u"def", + labels={}, annotations={}, + )) + + def test_invalid_name(self): + with pytest.raises(errors.Error, match="name"): + bonsai_asset.ActionModule.validate_arguments(dict( + name=1.234, version="1.2.3", + )) + + def test_missing_name(self): + with pytest.raises(errors.Error, match="name"): + bonsai_asset.ActionModule.validate_arguments(dict( + version="1.2.3", + )) + + def test_invalid_version(self): + with pytest.raises(errors.Error, match="version"): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version=1.2, + )) + + def test_missing_version(self): + with pytest.raises(errors.Error, match="version"): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", + )) + + def test_invalid_rename(self): + with pytest.raises(errors.Error, match="rename"): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version="1.2.3", rename=1, + )) + + def test_invalid_labels(self): + with pytest.raises(errors.Error, match="labels"): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version="1.2.3", labels=1, + )) + + def test_invalid_annotations(self): + with pytest.raises(errors.Error, match="annotations"): + bonsai_asset.ActionModule.validate_arguments(dict( + name="abc", version="1.2.3", annotations=1, + )) + + +class TestBuildAssetArgs: + def test_no_additional_metadata(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict(name="test/asset", version="1.2.3"), + dict(builds=[], labels=None, annotations=None), + ) + + assert result == dict( + name="test/asset", + state="present", + builds=[], + ) + + def test_bonsai_metadata_only(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict(name="test/asset", version="1.2.3"), + dict(builds=[], labels=dict(a="b"), annotations=dict(c="d")), + ) + + assert result == dict( + name="test/asset", + state="present", + builds=[], + annotations=dict(c="d"), + labels=dict(a="b"), + ) + + def test_user_metadata_only(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict( + name="test/asset", + version="1.2.3", + labels=dict(my="label"), + annotations=dict(my="annotation"), + ), + dict(builds=[1, 2, 3], labels=None, annotations=None), + ) + + assert result == dict( + name="test/asset", + state="present", + builds=[1, 2, 3], + annotations=dict(my="annotation"), + labels=dict(my="label"), + ) + + def test_mixed_metadata(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict( + name="test/asset", + version="1.2.3", + labels=dict(my="label"), + annotations=dict(my="annotation"), + ), + dict(builds=[], labels=dict(my="x", a="b"), annotations=dict(my="c")), + ) + + assert result == dict( + name="test/asset", + state="present", + builds=[], + annotations=dict(my="annotation"), + labels=dict(my="label", a="b"), + ) + + def test_rename(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict(name="test/asset", version="1.2.3", rename="my-asset"), + dict(builds=[], labels=None, annotations=None), + ) + + assert result == dict( + name="my-asset", + state="present", + builds=[], + ) + + def test_auth_passthrough(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict( + auth=dict(url="http://localhost:1234"), + name="test/asset", + version="1.2.3", + ), + dict(builds=[], labels=None, annotations=None), + ) + + assert result == dict( + auth=dict(url="http://localhost:1234"), + name="test/asset", + state="present", + builds=[], + ) + + def test_namespace_passthrough(self): + result = bonsai_asset.ActionModule.build_asset_args( + dict(namespace='default', name="test/asset", version="1.2.3"), + dict(builds=[], labels=None, annotations=None), + ) + + assert result == dict( + name="test/asset", + namespace='default', + state="present", + builds=[], + ) + + +class TestDownloadAssetDefinition: + def get_mock_action(self, mocker, result): + action = bonsai_asset.ActionModule( + mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock(), loader=None, + templar=None, shared_loader_obj=None, + ) + action._execute_module = mocker.MagicMock(return_value=result) + return action + + def test_download_on_control_node(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + bonsai_params.return_value = dict(sample="value") + action = self.get_mock_action(mocker, {}) + + result = action.download_asset_definition( + on_remote=False, name="test/asset", version="1.2.3", task_vars=None, + ) + + assert result == dict(sample="value") + bonsai_params.assert_called_once() + action._execute_module.assert_not_called() + + def test_fail_download_on_control_node(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + bonsai_params.side_effect = errors.BonsaiError("Bonsai bad") + action = self.get_mock_action(mocker, {}) + + with pytest.raises(errors.Error, match="Bonsai bad"): + action.download_asset_definition( + on_remote=False, name="test/asset", version="1.2.3", task_vars=None, + ) + + bonsai_params.assert_called_once() + action._execute_module.assert_not_called() + + def test_download_on_target_node(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + action = self.get_mock_action(mocker, dict(asset="sample")) + + result = action.download_asset_definition( + on_remote=True, name="test/asset", version="1.2.3", task_vars=None, + ) + + assert result == "sample" + bonsai_params.assert_not_called() + action._execute_module.assert_called_once() + + def test_fail_on_target_node(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + action = self.get_mock_action(mocker, dict(failed=True, msg="Bad err")) + + with pytest.raises(errors.Error, match="Bad err"): + action.download_asset_definition( + on_remote=True, name="test/asset", version="1.2.3", task_vars=None, + ) + + bonsai_params.assert_not_called() + action._execute_module.assert_called_once() + + +class TestRun: + def test_success(self, mocker): + task = mocker.MagicMock(Task, async_val=0, args=dict( + name="test/asset", + version="1.2.3", + )) + action = bonsai_asset.ActionModule( + task, mocker.MagicMock(), mocker.MagicMock(), loader=None, + templar=None, shared_loader_obj=None, + ) + action._execute_module = mocker.MagicMock(return_value=dict(a=3)) + action.download_asset_definition = mocker.MagicMock( + return_value=dict(builds=[], labels=None, annotations=None), + ) + + result = action.run() + + assert result == dict(a=3) + + def test_fail(self, mocker): + task = mocker.MagicMock(Task, async_val=0, args=dict( + name="test/asset", + )) + action = bonsai_asset.ActionModule( + task, mocker.MagicMock(), mocker.MagicMock(), loader=None, + templar=None, shared_loader_obj=None, + ) + action._execute_module = mocker.MagicMock(return_value=dict(a=3)) + action.download_asset_definition = mocker.MagicMock( + return_value=dict(builds=[], labels=None, annotations=None), + ) + + result = action.run() + + assert result["failed"] is True diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py new file mode 100644 index 000000000..261080e3b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.filter import backends + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestBackends: + def test_backends_in_groups_no_ssl(self): + hostvars = { + "1.2.3.4": {"inventory_hostname": "1.2.3.4"}, + "1.2.3.5": {"inventory_hostname": "1.2.3.5"}, + "1.2.3.6": {"inventory_hostname": "1.2.3.6"}, + "1.2.3.7": {"inventory_hostname": "1.2.3.7"}, + } + groups = {"backends": ["1.2.3.4", "1.2.3.5"]} + + assert backends.backends(hostvars, groups) == [ + "ws://1.2.3.4:8081", + "ws://1.2.3.5:8081", + ] + + def test_backends_in_groups_ssl(self): + hostvars = { + "1.2.3.4": {"inventory_hostname": "1.2.3.4"}, + "1.2.3.5": {"inventory_hostname": "1.2.3.5"}, + "1.2.3.6": { + "inventory_hostname": "1.2.3.6", + "api_key_file": "path/to/key.file", + }, + "1.2.3.7": {"inventory_hostname": "1.2.3.7"}, + } + groups = {"backends": ["1.2.3.6"]} + + assert backends.backends(hostvars, groups) == ["wss://1.2.3.6:8081"] + + def test_backends_not_in_groups(self): + hostvars = { + "1.2.3.4": {"inventory_hostname": "1.2.3.4"}, + "1.2.3.5": {"inventory_hostname": "1.2.3.5"}, + "1.2.3.6": {"inventory_hostname": "1.2.3.6"}, + "1.2.3.7": {"inventory_hostname": "1.2.3.7"}, + } + groups = {} + + assert backends.backends(hostvars, groups) == [] diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py new file mode 100644 index 000000000..794727abf --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py @@ -0,0 +1,58 @@ +# Copyright: (c) 2020, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.filter import package_name + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestPackageName: + def test_yum_latest_version(self): + assert "package" == package_name.package_name( + "yum", "package", "latest", "latest", + ) + + def test_yum_latest_build(self): + assert "package-123" == package_name.package_name( + "yum", "package", "123", "latest", + ) + + def test_yum_selected_build(self): + assert "package-123-456" == package_name.package_name( + "yum", "package", "123", "456", + ) + + def test_yum_ignore_build_if_latest_version(self): + assert "package" == package_name.package_name( + "yum", "package", "latest", "456", + ) + + def test_apt_latest_version(self): + assert "package" == package_name.package_name( + "apt", "package", "latest", "latest", + ) + + def test_apt_latest_build(self): + assert "package=123-*" == package_name.package_name( + "apt", "package", "123", "latest", + ) + + def test_apt_selected_build(self): + assert "package=123-456" == package_name.package_name( + "apt", "package", "123", "456", + ) + + def test_apt_ignore_build_if_latest_version(self): + assert "package" == package_name.package_name( + "apt", "package", "latest", "456", + ) diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py new file mode 100644 index 000000000..ae62f1f57 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py @@ -0,0 +1,158 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + arguments, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestGetSpec: + @pytest.mark.parametrize("param", [ + "auth", "state", "name", "labels", "annotations", + ]) + def test_valid_parameter(self, param): + assert set(arguments.get_spec(param).keys()) == set((param,)) + + def test_invalid_parameter(self): + with pytest.raises(KeyError): + arguments.get_spec("bad_parameter_name") + + def test_multiple_parameters(self): + assert set(arguments.get_spec("auth", "name", "labels").keys()) == set( + ("auth", "name", "labels") + ) + + +class TestGetSpecPayload: + def test_no_key(self): + params = dict( + name="name", + key="value" + ) + + assert arguments.get_spec_payload(params) == dict() + + def test_spec_payload(self): + params = dict( + name="name", + key="value", + ) + + assert arguments.get_spec_payload(params, "key") == dict( + key="value", + ) + + +class TestGetRenamedSpecPayload: + def test_no_mapping(self): + params = dict( + name="name", + key="value", + ) + assert arguments.get_renamed_spec_payload(params, dict()) == dict() + + def test_renamed_payload(self): + params = dict( + name="name", + key="value", + ) + mapping = dict( + name="new_name", + ) + assert arguments.get_renamed_spec_payload(params, mapping) == dict( + new_name="name", + ) + + +class TestGetMutationPayload: + def test_name_only(self): + params = dict( + name="name", + ) + + assert arguments.get_mutation_payload(params) == dict( + metadata=dict( + name="name", + ), + ) + + def test_name_and_namespace(self): + params = dict( + name="name", + namespace="space", + ) + + assert arguments.get_mutation_payload(params) == dict( + metadata=dict( + name="name", + namespace="space", + ), + ) + + def test_wanted_key(self): + params = dict( + name="name", + key="value", + ) + + assert arguments.get_mutation_payload(params, "key") == dict( + key="value", + metadata=dict( + name="name", + ), + ) + + def test_namespace_is_none(self): + params = dict( + name="name", + namespace=None, + ) + + with pytest.raises(AssertionError, match="BUG"): + arguments.get_mutation_payload(params) + + def test_labels(self): + params = dict( + name="name", + labels=dict( + some="label", + numeric=3, + ), + ) + + assert arguments.get_mutation_payload(params) == dict( + metadata=dict( + name="name", + labels=dict( + some="label", + numeric="3", + ), + ), + ) + + def test_annotations(self): + params = dict( + name="name", + annotations=dict( + my="Annotation", + number=45, + ), + ) + + assert arguments.get_mutation_payload(params) == dict( + metadata=dict( + name="name", + annotations=dict( + my="Annotation", + number="45", + ), + ), + ) diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py new file mode 100644 index 000000000..cf0d8a492 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + bonsai, errors, http, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestGet: + def test_url_construction(self, mocker): + http_mock = mocker.patch.object(bonsai, "http") + http_mock.request.return_value = http.Response(200, "{}") + + bonsai.get("path") + + assert http_mock.request.call_args[0] == ( + "GET", "https://bonsai.sensu.io/api/v1/assets/path", + ) + + def test_bad_status(self, mocker): + http_mock = mocker.patch.object(bonsai, "http") + http_mock.request.return_value = http.Response(400, "{}") + + with pytest.raises(errors.BonsaiError, match="400"): + bonsai.get("path") + + def test_invalid_json(self, mocker): + http_mock = mocker.patch.object(bonsai, "http") + http_mock.request.return_value = http.Response(200, "{ a }") + + with pytest.raises(errors.BonsaiError, match="JSON"): + bonsai.get("path") + + +class TestGetAvailableAssetVersions: + def test_valid_data(self, mocker): + get = mocker.patch.object(bonsai, "get") + get.return_value = dict( + versions=[ + dict(version="1.2.3", assets=[]), + dict(version="1.2.4", assets=[]), + dict(version="1.2.5", assets=[]), + ], + ) + + result = bonsai.get_available_asset_versions("namespace", "name") + + assert set(("1.2.3", "1.2.4", "1.2.5")) == result + assert get.call_args[0] == ("namespace/name",) + + @pytest.mark.parametrize("data", [ + "invalid", + dict(invalid="toplevel"), + dict(versions="oh-no"), + dict(versions=["not", "ok"]), + dict(versions=[dict(invalid="internal")]), + ]) + def test_invalid_data(self, mocker, data): + get = mocker.patch.object(bonsai, "get") + get.return_value = data + + with pytest.raises(errors.BonsaiError, match="versions"): + bonsai.get_available_asset_versions("namespace", "name") + + +class TestGetAssetVersionBuilds: + def test_url_construction(self, mocker): + get = mocker.patch.object(bonsai, "get") + get.return_value = dict(spec=dict(builds=[])) + + bonsai.get_asset_version_builds("x", "y", "z") + + assert get.call_args[0] == ("x/y/z/release_asset_builds",) + + @pytest.mark.parametrize("data", [ + "invalid", + dict(missing="spec"), + dict(spec="invalid"), + dict(spec=dict(missing="builds")), + ]) + def test_invalid_data(self, mocker, data): + get = mocker.patch.object(bonsai, "get") + get.return_value = data + + with pytest.raises(errors.BonsaiError, match="spec"): + bonsai.get_asset_version_builds("x", "y", "z") + + +class TestGetAssetParameters: + def test_valid_all_data(self, mocker): + versions = mocker.patch.object(bonsai, "get_available_asset_versions") + versions.return_value = set(("t", "u", "v")) + builds = mocker.patch.object(bonsai, "get_asset_version_builds") + builds.return_value = dict( + metadata=dict( + annotations=dict(annotation="value"), + labels=dict(label="value"), + ), + spec=dict(builds=[1, 2, 3]), + ) + + result = bonsai.get_asset_parameters("x/y", "v") + + assert result == dict( + labels=dict(label="value"), + annotations=dict(annotation="value"), + builds=[1, 2, 3], + ) + assert versions.call_args[0] == ("x", "y") + assert builds.call_args[0] == ("x", "y", "v") + + def test_valid_minimal_data(self, mocker): + versions = mocker.patch.object(bonsai, "get_available_asset_versions") + versions.return_value = set(("t", "u", "v")) + builds = mocker.patch.object(bonsai, "get_asset_version_builds") + builds.return_value = dict( + spec=dict(builds=[1, 2, 3]), + ) + + result = bonsai.get_asset_parameters("x/y", "v") + + assert result == dict( + labels=None, + annotations=None, + builds=[1, 2, 3], + ) + assert versions.call_args[0] == ("x", "y") + assert builds.call_args[0] == ("x", "y", "v") + + def test_invalid_name(self, mocker): + with pytest.raises(errors.BonsaiError, match="names"): + bonsai.get_asset_parameters("x.y", "v") + + def test_invalid_version(self, mocker): + versions = mocker.patch.object(bonsai, "get_available_asset_versions") + versions.return_value = set(("t", "u")) + + with pytest.raises(errors.BonsaiError, match="Version"): + bonsai.get_asset_parameters("x/y", "v") diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py new file mode 100644 index 000000000..ecc6fc54b --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + client, errors, http +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestAuthHeader: + def test_using_valid_token(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, '{"access_token": "token"}') + + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + + assert dict(Authorization="Bearer token") == c.auth_header + assert 1 == request.call_count + assert ("GET", "http://example.com/auth") == request.call_args[0] + assert "user" == request.call_args[1]["url_username"] + assert "pass" == request.call_args[1]["url_password"] + + def test_cache_auth_headers_with_token(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, '{"access_token": "token"}') + + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + for i in range(5): + c.auth_header + + assert 1 == request.call_count + + def test_login_failure_token_bad_status(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(500, '{"access_token": "token"}') + + with pytest.raises(errors.SensuError, match="500"): + client.Client( + "http://example.com/", "user", "pass", None, True, None, + ).auth_header + + def test_login_failure_token_bad_json(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, "{ not a json }") + + with pytest.raises(errors.SensuError, match="JSON"): + client.Client( + "http://example.com/", "user", "pass", None, True, None, + ).auth_header + + def test_login_failure_token_missing_token(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, '{"access_bla": "token"}') + + with pytest.raises(errors.SensuError, match="token"): + client.Client( + "http://example.com/", "user", "pass", None, True, None, + ).auth_header + + +class TestVersion: + def test_valid_version(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + mocker.patch.object(c, "get").return_value = http.Response( + 200, '{"sensu_backend":"5.21.0#sha-here"}', + ) + + assert c.version == "5.21.0" + + def test_valid_version_is_cached(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + get = mocker.patch.object(c, "get") + get.return_value = http.Response( + 200, '{"sensu_backend":"5.21.0#sha-here"}', + ) + + for i in range(4): + c.version + + get.assert_called_once() + + def test_non_200_response(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + mocker.patch.object(c, "get").return_value = http.Response( + 400, '{"sensu_backend":"5.21.0#sha-here"}', + ) + + with pytest.raises(errors.SensuError, match="400"): + c.version + + def test_bad_json_response(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + mocker.patch.object(c, "get").return_value = http.Response( + 200, '"sensu_backend', + ) + + with pytest.raises(errors.SensuError, match="JSON"): + c.version + + def test_missing_backend_version_in_response(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + mocker.patch.object(c, "get").return_value = http.Response(200, '{}') + + with pytest.raises(errors.SensuError, match="backend"): + c.version + + def test_invalid_version(self, mocker): + c = client.Client("http://example.com/", "u", "p", None, True, None) + mocker.patch.object(c, "get").return_value = http.Response( + 200, '{"sensu_backend":"devel"}', + ) + + assert c.version == c.BAD_VERSION + + +class TestRequest: + def test_request_payload_token(self, mocker): + request = mocker.patch.object(http, "request") + request.side_effect = ( + http.Response(200, '{"access_token": "token"}'), + http.Response(200, "data"), + ) + + client.Client( + "http://example.com/", "user", "pass", None, True, None, + ).request("PUT", "/path", dict(some="payload")) + + request.assert_called_with( + "PUT", "http://example.com/path", + payload=dict(some="payload"), + headers=dict(Authorization="Bearer token"), + validate_certs=True, + ca_path=None, + ) + + def test_request_payload_api_key(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, "data") + + client.Client( + "http://example.com/", None, None, "key", False, None, + ).request("PUT", "/path", dict(some="payload")) + + request.assert_called_once_with( + "PUT", "http://example.com/path", + payload=dict(some="payload"), + headers=dict(Authorization="Key key"), + validate_certs=False, + ca_path=None, + ) + + def test_request_no_payload_token(self, mocker): + request = mocker.patch.object(http, "request") + request.side_effect = ( + http.Response(200, '{"access_token": "token"}'), + http.Response(200, "data"), + ) + + client.Client( + "http://example.com/", "user", "pass", None, True, "/ca", + ).request("PUT", "/path") + + request.assert_called_with( + "PUT", "http://example.com/path", payload=None, + headers=dict(Authorization="Bearer token"), + validate_certs=True, + ca_path="/ca", + ) + + def test_request_no_payload_api_key(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, "data") + + client.Client( + "http://example.com/", "u", "p", "key", False, "/ca", + ).request("PUT", "/path") + + request.assert_called_once_with( + "PUT", "http://example.com/path", payload=None, + headers=dict(Authorization="Key key"), + validate_certs=False, + ca_path="/ca", + ) + + @pytest.mark.parametrize("status", [401, 403]) + def test_request_bad_credentials(self, status, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(status, "data") + + with pytest.raises(errors.SensuError, match="credentials"): + client.Client( + "http://example.com/", None, None, "key", True, None, + ).request("PUT", "/path", dict(some="payload")) + + request.assert_called_once_with( + "PUT", "http://example.com/path", + payload=dict(some="payload"), + headers=dict(Authorization="Key key"), + validate_certs=True, + ca_path=None, + ) + + +class TestGet: + def test_get(self, mocker): + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + c.request = mocker.Mock() + + c.get("/path") + + c.request.assert_called_with("GET", "/path") + + +class TestPut: + def test_put(self, mocker): + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + c.request = mocker.Mock() + + c.put("/path", {}) + + c.request.assert_called_with("PUT", "/path", {}) + + +class TestDelete: + def test_delete(self, mocker): + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + c.request = mocker.Mock() + + c.delete("/path") + + c.request.assert_called_with("DELETE", "/path") + + +class TestValidateAuthData: + def test_valid_creds(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(200, None) + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + + result = c.validate_auth_data("check_user", "check_pass") + + assert result + assert 1 == request.call_count + assert ("GET", "http://example.com/auth/test") == request.call_args[0] + assert "check_user" == request.call_args[1]["url_username"] + assert "check_pass" == request.call_args[1]["url_password"] + + def test_invalid_creds(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(401, None) + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + + result = c.validate_auth_data("check_user", "check_pass") + + assert not result + assert 1 == request.call_count + assert ("GET", "http://example.com/auth/test") == request.call_args[0] + assert "check_user" == request.call_args[1]["url_username"] + assert "check_pass" == request.call_args[1]["url_password"] + + def test_broken_backend(self, mocker): + request = mocker.patch.object(http, "request") + request.return_value = http.Response(500, None) + c = client.Client( + "http://example.com/", "user", "pass", None, True, None, + ) + + with pytest.raises(errors.SensuError, match="500"): + c.validate_auth_data("check_user", "check_pass") diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py new file mode 100644 index 000000000..84fddb53a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import ssl +import sys + +import pytest + +from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, http, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestResponse: + def test_with_valid_json(self): + resp = http.Response(201, '{"some": ["json", "data", 3]}') + + assert 201 == resp.status + assert '{"some": ["json", "data", 3]}' == resp.data + assert {"some": ["json", "data", 3]} == resp.json + + def test_with_invalid_json(self): + resp = http.Response(404, "") + + assert 404 == resp.status + assert "" == resp.data + assert resp.json is None + + +class TestRequest: + def test_ok_request(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + resp = http.request("GET", "example.com/path") + + assert 200 == resp.status + assert "data" == resp.data + assert "GET" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + + def test_non_20x_status(self, mocker): + open_url = mocker.patch.object(http, "open_url") + open_url.side_effect = HTTPError( + "url", 404, "missing", {}, None, + ) + + resp = http.request("GET", "example.com/bad") + + assert 404 == resp.status + assert "missing" == resp.data + assert "GET" == open_url.call_args[1]["method"] + assert "example.com/bad" == open_url.call_args[1]["url"] + + def test_url_error(self, mocker): + open_url = mocker.patch.object(http, "open_url") + open_url.side_effect = URLError("Invalid") + + with pytest.raises(errors.HttpError): + http.request("GET", "example.com/bad") + + def test_payload_no_headers(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + http.request("PUT", "example.com/path", payload=dict(a=2)) + + assert "PUT" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + assert '{"a":2}' == open_url.call_args[1]["data"] + headers = open_url.call_args[1]["headers"] + assert {"content-type": "application/json"} == headers + + def test_payload_with_headers(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + http.request( + "PUT", "example.com/path", payload=dict(b=4), headers=dict(h="v"), + ) + + assert "PUT" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + assert '{"b":4}' == open_url.call_args[1]["data"] + headers = open_url.call_args[1]["headers"] + assert {"content-type": "application/json", "h": "v"} == headers + + def test_payload_overrides_data(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + http.request( + "PUT", "example.com/path", payload=dict(a=2), data="data", + ) + + assert "PUT" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + assert '{"a":2}' == open_url.call_args[1]["data"] + headers = open_url.call_args[1]["headers"] + assert {"content-type": "application/json"} == headers + + def test_data(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + http.request("PUT", "example.com/path", data="data") + + assert "PUT" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + assert "data" == open_url.call_args[1]["data"] + assert open_url.call_args[1]["headers"] is None + + def test_kwargs(self, mocker): + data_resp = mocker.Mock() + data_resp.read.return_value = "data" + data_resp.getcode.return_value = 200 + open_url = mocker.patch.object(http, "open_url") + open_url.return_value = data_resp + + http.request("PUT", "example.com/path", a=3, b="f") + + assert "PUT" == open_url.call_args[1]["method"] + assert "example.com/path" == open_url.call_args[1]["url"] + assert 3 == open_url.call_args[1]["a"] + assert "f" == open_url.call_args[1]["b"] + + def test_cert_error_ssl_module_present(self, mocker): + open_url = mocker.patch.object(http, "open_url") + open_url.side_effect = ssl.CertificateError("Invalid") + + with pytest.raises(errors.HttpError): + http.request("GET", "example.com/bad") + + def test_cert_error_ssl_module_absent(self, mocker): + class Dummy(Exception): + pass + + open_url = mocker.patch.object(http, "open_url") + open_url.side_effect = ssl.CertificateError("Invalid") + mocker.patch.object(http, "CertificateError", Dummy) + + with pytest.raises(ssl.CertificateError): + http.request("GET", "example.com/bad") diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py new file mode 100644 index 000000000..e4f6ff578 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import role_utils + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoSubjectsDiffer: + def test_different_lengths(self): + assert role_utils._do_subjects_differ( + [{"type": "a", "name": "a"}], + [{"type": "a", "name": "a"}, {"type": "a", "name": "b"}] + ) is True + + def test_different_type_with_same_name(self): + assert role_utils._do_subjects_differ( + [{"type": "a", "name": "same"}], + [{"type": "b", "name": "same"}] + ) is True + + def test_equal_with_different_order_within_type(self): + assert role_utils._do_subjects_differ( + [{"type": "a", "name": "a2"}, {"type": "a", "name": "a1"}], + [{"type": "a", "name": "a1"}, {"type": "a", "name": "a2"}] + ) is False + + def test_equal_with_different_order_multiple_types(self): + assert role_utils._do_subjects_differ( + [ + {"type": "a", "name": "a2"}, + {"type": "b", "name": "b1"}, + {"type": "a", "name": "a1"} + ], + [ + {"type": "a", "name": "a1"}, + {"type": "a", "name": "a2"}, + {"type": "b", "name": "b1"} + ] + ) is False + + def test_different(self): + assert role_utils._do_subjects_differ( + [ + {"type": "a", "name": "a2"}, + {"type": "b", "name": "b3"}, + ], + [ + {"type": "c", "name": "c2"}, + {"type": "a", "name": "s2"}, + ] + ) is True + + +class TestDoRoleBindingsDiffer: + def test_equal_role_binding(self): + current = { + 'role_ref': 'a', + 'subjects': [ + {"type": "User", "name": "b"}, + {"type": "Group", "name": "g"}, + ] + } + desired = { + 'role_ref': 'a', + 'subjects': [ + {"type": "User", "name": "b"}, + {"type": "Group", "name": "g"}, + ] + } + assert role_utils.do_role_bindings_differ(current, desired) is False + + def test_equal_role_binding_mixed_users_and_groups(self): + current = { + 'role_ref': 'a', + 'subjects': [ + {"type": "Group", "name": "g1"}, + {"type": "User", "name": "u1"}, + {"type": "Group", "name": "g2"}, + {"type": "User", "name": "u2"}, + ] + } + desired = { + 'role_ref': 'a', + 'subjects': [ + {"type": "User", "name": "u2"}, + {"type": "User", "name": "u1"}, + {"type": "Group", "name": "g2"}, + {"type": "Group", "name": "g1"}, + ] + } + assert role_utils.do_role_bindings_differ(current, desired) is False + + def test_updated_role_binding_subjects(self): + current = { + 'role_ref': 'a', + 'subjects': [ + {"type": "User", "name": "b"}, + ] + } + desired = { + 'role_ref': 'a', + 'subjects': [ + {"type": "User", "name": "b"}, + {"type": "Group", "name": "g"}, + ] + } + assert role_utils.do_role_bindings_differ(current, desired) is True + + +class TestRuleSets: + def test_all_keys_none(self): + assert role_utils._rule_set([{}]) == set( + ((frozenset(), frozenset(), frozenset()),) + ) + + def test_rules_multiple(self): + assert role_utils._rule_set([{ + 'verbs': ['list', 'get'], + 'resources': ['entities', 'checks'], + 'resource_names': None + }, { + 'verbs': ['list', 'delete'], + 'resources': ['entities', 'checks'], + 'resource_names': None + }]) == set(( + (frozenset(['delete', 'list']), frozenset(['checks', 'entities']), frozenset()), + (frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset()) + )) + + def test_missing_key(self): + assert role_utils._rule_set([{ + 'verbs': ['list', 'get'], + 'resources': ['entities', 'checks'], + }]) == set( + ((frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset()),) + ) + + +class TestDoRulesDiffer: + def test_empty_values(self): + assert role_utils._do_rules_differ( + [{'verbs': []}], + [{'verbs': []}] + ) is False + + def test_rules_when_current_values_are_none(self): + assert role_utils._do_rules_differ( + [{'verbs': None}], + [{'verbs': ['get', 'list']}] + ) is True + + def test_rules_when_desired_values_are_none(self): + assert role_utils._do_rules_differ( + [{'verbs': ['get', 'list']}], + [{'verbs': None}] + ) is True + + def test_rules_are_different(self): + assert role_utils._do_rules_differ( + [{'verbs': ['list', 'get']}], + [{'verbs': ['get', 'delete']}] + ) is True + + def test_rules_with_additional_keys_in_current(self): + assert role_utils._do_rules_differ( + [{'verbs': ['list', 'get'], 'resources': ['checks', 'entities']}], + [{'verbs': ['get', 'list']}] + ) is True + + def test_rules_are_the_same(self): + assert role_utils._do_rules_differ( + [{'verbs': ['list', 'get']}], + [{'verbs': ['get', 'list']}] + ) is False + + +class TestDoRolesDiffer: + def test_rules_when_values_in_current_are_none(self): + current = { + 'rules': [{ + 'resource_names': None + }] + } + desired = { + 'rules': [{ + 'resource_names': ['check-cpu'] + }] + } + assert role_utils.do_roles_differ(current, desired) is True + + def test_rules_when_values_in_desired_are_none(self): + current = { + 'rules': [{ + 'resource_names': ['check-cpu'] + }] + } + desired = { + 'rules': [{ + 'resource_names': None + }] + } + assert role_utils.do_roles_differ(current, desired) is True + + def test_different_rules_order(self): + current = { + 'rules': [{ + 'verbs': ['get', 'list'], + 'resources': ['entities', 'checks'] + }, { + 'verbs': ['create', 'delete', 'update'], + 'resources': ['assets', 'hooks'] + }] + } + desired = { + 'rules': [{ + 'verbs': ['delete', 'create', 'update'], + 'resources': ['hooks', 'assets'] + }, { + 'verbs': ['list', 'get'], + 'resources': ['checks', 'entities'] + }] + } + assert role_utils.do_roles_differ(current, desired) is False + + def test_key_missing_in_current(self): + current = { + 'rules': [{ + 'verbs': ['update', 'create'], + 'resources': ['hooks', 'assets'] + }] + } + desired = { + 'rules': [{ + 'verbs': ['create', 'update'], + 'resources': ['hooks', 'assets'], + 'resource_names': ['check-cpu'] + }] + } + assert role_utils.do_roles_differ(current, desired) is True + + def test_key_missing_in_desired(self): + current = { + 'rules': [{ + 'verbs': ['update', 'create'], + 'resources': ['hooks', 'assets'], + 'resource_names': ['check-cpu'] + }] + } + desired = { + 'rules': [{ + 'verbs': ['create', 'update'], + 'resources': ['hooks', 'assets'] + }] + } + assert role_utils.do_roles_differ(current, desired) is True + + def test_role_exists_but_with_additional_rules(self): + current = { + 'rules': [{ + 'verbs': ['get', 'list'], + 'resources': ['entities', 'check'] + }, { + 'verbs': ['create', 'update', 'delete'], + 'resources': ['assets', 'hooks'] + }] + } + desired = { + 'rules': [{ + 'verbs': ['list', 'get'], + 'resources': ['check', 'entities'] + }] + } + assert role_utils.do_roles_differ(current, desired) is True diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py new file mode 100644 index 000000000..f737dce0a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py @@ -0,0 +1,491 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si> +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, http, utils, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSync: + def test_absent_no_current_object(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + changed, object = utils.sync("absent", client, "/path", {}, False) + + assert changed is False + assert object is None + + def test_absent_no_current_object_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + changed, object = utils.sync("absent", client, "/path", {}, True) + + assert changed is False + assert object is None + + def test_absent_current_object_present(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + client.delete.return_value = http.Response(204, "") + + changed, object = utils.sync("absent", client, "/path", {}, False) + + assert changed is True + assert object is None + client.delete.assert_called_with("/path") + + def test_absent_current_object_present_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + client.delete.return_value = http.Response(204, "") + + changed, object = utils.sync("absent", client, "/path", {}, True) + + assert changed is True + assert object is None + client.delete.assert_not_called() + + def test_present_no_current_object(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(404, ""), + http.Response(200, '{"new": "data"}'), + ) + client.put.return_value = http.Response(201, "") + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, False, + ) + + assert changed is True + assert {"new": "data"} == object + client.put.assert_called_once_with("/path", {"my": "data"}) + + def test_present_no_current_object_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, True, + ) + + assert changed is True + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_current_object_differ(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(200, '{"current": "data"}'), + http.Response(200, '{"new": "data"}'), + ) + client.put.return_value = http.Response(201, "") + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, False, + ) + + assert changed is True + assert {"new": "data"} == object + client.put.assert_called_once_with("/path", {"my": "data"}) + + def test_present_current_object_differ_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"current": "data"}') + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, True, + ) + + assert changed is True + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_current_object_does_not_differ(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"my": "data"}') + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, False, + ) + + assert changed is False + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_current_object_does_not_differ_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"my": "data"}') + + changed, object = utils.sync( + "present", client, "/path", {"my": "data"}, True, + ) + + assert changed is False + assert {"my": "data"} == object + client.put.assert_not_called() + + +class TestSyncV1: + def test_parameter_passthrough(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = (True, { + "metadata": {"name": "test", "namespace": "space"}, + "spec": {"key": "value"}, + }) + + changed, object = utils.sync_v1("absent", "c", "/path", {}, False) + + assert changed is True + assert { + "metadata": {"name": "test", "namespace": "space"}, + "key": "value", + } + + +class TestDoDiffer: + def test_extra_keys_in_current_do_not_matter(self): + assert utils.do_differ({"a": "b", "c": 3}, {"a": "b"}) is False + + def test_detect_different_values(self): + assert utils.do_differ({"a": "b"}, {"a": "c"}) is True + + def test_detect_missing_keys_in_current(self): + assert utils.do_differ({"a": "b"}, {"c": "d"}) is True + + def test_desired_none_values_are_ignored(self): + assert utils.do_differ({"a": "b"}, {"c": None}) is False + + def test_metadata_ignores_created_by(self): + assert utils.do_differ( + dict(metadata=dict(a=1, created_by=2)), + dict(metadata=dict(a=1)), + ) is False + + def test_metadata_detects_change(self): + assert utils.do_differ( + dict(metadata=dict(a=1)), dict(metadata=dict(a=2)), + ) is True + + def test_metadata_detects_change_in_presence_of_created_by(self): + assert utils.do_differ( + dict(metadata=dict(a=1, created_by=2)), + dict(metadata=dict(a=2)), + ) is True + + def test_ignore_keys_do_not_affect_the_outcome(self): + assert utils.do_differ(dict(a=1), dict(a=2), "a") is False + + def test_ignore_keys_do_not_mask_other_differences(self): + assert utils.do_differ(dict(a=1, b=1), dict(a=2, b=2), "a") is True + + +class TestDoDifferV1: + def test_extra_keys_in_current_do_not_matter(self): + assert utils.do_differ_v1( + {"spec": {"a": "b", "c": 3}}, {"spec": {"a": "b"}}, + ) is False + + def test_detect_different_values(self): + assert utils.do_differ_v1( + {"spec": {"a": "b"}}, {"spec": {"a": "c"}}, + ) is True + + def test_detect_missing_keys_in_current(self): + assert utils.do_differ_v1( + {"spec": {"a": "b"}}, {"spec": {"c": "d"}}, + ) is True + + def test_desired_none_values_are_ignored(self): + assert utils.do_differ_v1( + {"spec": {"a": "b"}}, {"spec": {"c": None}}, + ) is False + + def test_metadata_ignores_created_by(self): + assert utils.do_differ_v1( + {"metadata": {"a": 1, "created_by": 2}}, + {"metadata": {"a": 1}}, + ) is False + + def test_metadata_detects_change(self): + assert utils.do_differ_v1( + {"metadata": {"a": 1}}, {"metadata": {"a": 2}}, + ) is True + + def test_metadata_detects_change_in_presence_of_created_by(self): + assert utils.do_differ_v1( + {"metadata": {"a": 1, "created_by": 2}}, + {"metadata": {"a": 2}}, + ) is True + + def test_ignore_keys_do_not_affect_the_outcome(self): + assert utils.do_differ_v1( + {"spec": {"a": 1}}, {"spec": {"a": 2}}, "a", + ) is False + + def test_ignore_keys_do_not_mask_other_differences(self): + assert utils.do_differ_v1( + {"spec": {"a": 1, "b": 1}}, {"spec": {"a": 2, "b": 2}}, "a", + ) is True + + +class TestGet: + @pytest.mark.parametrize( + "status", [100, 201, 202, 203, 204, 400, 401, 403, 500, 501], + ) + def test_abort_on_invalid_status(self, mocker, status): + client = mocker.Mock() + client.get.return_value = http.Response(status, "") + + with pytest.raises(errors.SyncError, match=str(status)): + utils.get(client, "/get") + client.get.assert_called_once_with("/get") + + def test_abort_on_invalid_json(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, "") + + with pytest.raises(errors.SyncError, match="JSON"): + utils.get(client, "/get") + client.get.assert_called_once_with("/get") + + def test_ignore_invalid_json_on_404(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + object = utils.get(client, "/get") + + assert object is None + client.get.assert_called_once_with("/get") + + def test_valid_json(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"get": "data"}') + + object = utils.get(client, "/get") + + assert {"get": "data"} == object + client.get.assert_called_once_with("/get") + + +class TestDelete: + @pytest.mark.parametrize( + "status", [100, 200, 201, 202, 203, 400, 401, 403, 500, 501], + ) + def test_abort_on_invalid_status(self, mocker, status): + client = mocker.Mock() + client.delete.return_value = http.Response(status, "") + + with pytest.raises(errors.SyncError, match=str(status)): + utils.delete(client, "/delete") + client.delete.assert_called_once_with("/delete") + + def test_valid_delete(self, mocker): + client = mocker.Mock() + client.delete.return_value = http.Response(204, "{}") + + object = utils.delete(client, "/delete") + + assert object is None + client.delete.assert_called_once_with("/delete") + + +class TestPut: + @pytest.mark.parametrize( + "status", [100, 202, 203, 204, 400, 401, 403, 500, 501], + ) + def test_abort_on_invalid_status(self, mocker, status): + client = mocker.Mock() + client.put.return_value = http.Response(status, "") + + with pytest.raises(errors.SyncError, match=str(status)): + utils.put(client, "/put", {"payload": "data"}) + client.put.assert_called_once_with("/put", {"payload": "data"}) + + @pytest.mark.parametrize("status", [200, 201]) + def test_valid_put(self, mocker, status): + client = mocker.Mock() + client.put.return_value = http.Response(status, '{"put": "data"}') + + object = utils.put(client, "/put", {"payload": "data"}) + + assert object is None + client.put.assert_called_once_with("/put", {"payload": "data"}) + + +class TestDictToSingleItemDicts: + def test_conversion(self): + result = utils.dict_to_single_item_dicts({"a": 0, 1: "b"}) + + assert 2 == len(result) + for item in ({"a": 0}, {1: "b"}): + assert item in result + + +class TestSingleItemDictsToDict: + def test_conversion(self): + assert dict(a=3, b=4, c=5) == utils.single_item_dicts_to_dict( + [dict(a=3), dict(b=4), dict(c=5)] + ) + + +class TestDictToKeyValueString: + def test_conversion(self): + result = utils.dict_to_key_value_strings({"a": 0, 1: "b"}) + + assert set(("a=0", "1=b")) == set(result) + + +class TestBuildUrlPath: + @pytest.mark.parametrize("parts,expectation", [ + ((), "/"), + ((None, None), "/"), + ((None, "a", "b", None, None, "c"), "/a/b/c"), + (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"), + (("/", " ", "a"), "/%2F/%20/a"), + ]) + def test_build_url_path_no_namespace(self, parts, expectation): + path = "/api/enterprise/store/v1" + expectation + assert path == utils.build_url_path( + "enterprise/store", "v1", None, *parts + ) + + @pytest.mark.parametrize("parts,expectation", [ + ((), "/"), + ((None, None), "/"), + ((None, "a", "b", None, None, "c"), "/a/b/c"), + (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"), + (("/", " ", "a"), "/%2F/%20/a"), + ]) + def test_build_url_path_with_namespace(self, parts, expectation): + path = "/api/core/v2/namespaces/default" + expectation + assert path == utils.build_url_path( + "core", "v2", "default", *parts + ) + + +class TestBuildCoreV2Path: + def test_build_path_no_namespace(self): + assert utils.build_core_v2_path(None, "a").startswith( + "/api/core/v2/", + ) + + def test_build_url_with_namespace(self): + assert utils.build_core_v2_path("default", "a").startswith( + "/api/core/v2/namespaces/default/", + ) + + +class TestPrepareResultList: + @pytest.mark.parametrize("input,output", [ + (None, []), # this is mosti likely result of a 404 status + ("a", ["a"]), + ([], []), + ([1, 2, 3], [1, 2, 3]), + ([None], [None]), # we leave lists intact, even if they contain None + ]) + def test_list_construction(self, input, output): + assert output == utils.prepare_result_list(input) + + +class TestConvertV1ToV2Response: + def test_none_passes_through(self): + assert utils.convert_v1_to_v2_response(None) is None + + def test_spec_only_if_metadata_is_missing(self): + assert utils.convert_v1_to_v2_response(dict( + spec=dict(a=1, b=2), + )) == dict(a=1, b=2) + + def test_add_metadata_from_toplevel(self): + assert utils.convert_v1_to_v2_response(dict( + metadata=dict(name="sample"), + spec=dict(a=1, b=2), + )) == dict(metadata=dict(name="sample"), a=1, b=2) + + +class TestDoSecretsDiffer: + @pytest.mark.parametrize("current,desired", [ + ( # All empty + [], [], + ), + ( # All is equal + [dict(name="a", secret="1"), dict(name="b", secret="2")], + [dict(name="a", secret="1"), dict(name="b", secret="2")], + ), + ( # Different order + [dict(name="a", secret="1"), dict(name="b", secret="2")], + [dict(name="b", secret="2"), dict(name="a", secret="1")], + ), + ]) + def test_no_difference(self, current, desired): + assert utils.do_secrets_differ( + dict(secrets=current), dict(secrets=desired), + ) is False + + @pytest.mark.parametrize("current,desired", [ + ( # Different source for variable b + [dict(name="b", secret="2")], [dict(name="b", secret="3")], + ), + ( # Different name + [dict(name="a", secret="1")], [dict(name="b", secret="1")], + ), + ( # Different number of secrets + [dict(name="a", secret="1"), dict(name="b", secret="2")], + [dict(name="a", secret="1")], + ), + ]) + def test_difference(self, current, desired): + assert utils.do_secrets_differ( + dict(secrets=current), dict(secrets=desired), + ) is True + + @pytest.mark.parametrize("secrets,diff", [ + # Missing secrets and empty list are the same + ([], False), + # None secrets are treated as empy list of secrets + (None, False), + # If anything is set, we have difference + ([dict(name="n", secret="s")], True), + ]) + def test_missing_secrets(self, secrets, diff): + assert utils.do_secrets_differ(dict(), dict(secrets=secrets)) is diff + assert utils.do_secrets_differ(dict(secrets=secrets), dict()) is diff + + +class TestDeprecate: + def test_ansible_lt_2_9_10(self, mocker): + module = mocker.MagicMock() + module.deprecate.side_effect = ( + TypeError("Simulating Ansible 2.9.9 and older"), + None, # Success, since no exception is raised + ) + + utils.deprecate(module, "Test msg", "3.2.1") + + assert module.deprecate.call_count == 2 + assert module.deprecate.called_once_with("Test msg", version="3.2.1") + + def test_ansible_ge_2_9_10(self, mocker): + module = mocker.MagicMock() + + utils.deprecate(module, "Test msg", "3.2.1") + + assert module.deprecate.called_once_with( + "Test msg", version="3.2.1", collection_name="sensu.sensu_go", + ) diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py new file mode 100644 index 000000000..2287dbc9e --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import json + +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + +from mock import patch + + +def set_module_args(**args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase: + def setup_method(self): + self.mock_module = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json, + ) + self.mock_module.start() + + def teardown_method(self): + self.mock_module.stop() + + +def generate_name(test_case): + return test_case['name'] diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py new file mode 100644 index 000000000..a7204bb5a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py @@ -0,0 +1,332 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, + utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import ad_auth_provider + +from .common.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + def test_no_changes(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict(name="activedirectory"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="activedirectory", + created_by="me", + ), + ) + + assert ad_auth_provider.do_differ(current, desired) is False + + def test_changes_are_detected(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=636, + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict(name="activedirectory"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="activedirectory", + created_by="me", + ), + ) + assert ad_auth_provider.do_differ(current, desired) is True + + def test_changes_are_detected_diff_servers_len(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ), + dict( + host="127.0.0.2", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ), + ], + ), + metadata=dict(name="activedirectory"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="activedirectory", + created_by="me", + ), + ) + assert ad_auth_provider.do_differ(current, desired) is True + + def test_changes_are_other_params(self): + desired = dict( + spec=dict( + servers=[], + groups_prefix="ad", + username_prefix="ad", + ), + metadata=dict(name="activedirectory"), + ) + current = dict( + spec=dict( + servers=[], + ), + metadata=dict( + name="activedirectory", + created_by="me", + ), + ) + assert ad_auth_provider.do_differ(current, desired) is True + + +class TestADAutProvider(ModuleTestCase): + def test_minimal_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="activedirectory", + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ) + + with pytest.raises(AnsibleExitJson): + ad_auth_provider.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[ + 0 + ] + + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory" + assert payload == dict( + type="ad", + api_version="authentication/v2", + metadata=dict(name="activedirectory"), + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=None, + insecure=False, + security="tls", + trusted_ca_file=None, + client_cert_file=None, + client_key_file=None, + default_upn_domain=None, + include_nested_groups=None, + binding=None, + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="group", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="sAMAccountName", + name_attribute="displayName", + object_class="person", + ), + ) + ] + ), + ) + + assert check_mode is False + + def test_all_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="activedirectory", + servers=[ + dict( + host="127.0.0.1", + port=636, + insecure=False, + security="tls", + trusted_ca_file="/path/to/trusted-certificate-authorities.pem", + client_cert_file="/path/to/ssl/cert.pem", + client_key_file="/path/to/ssl/key.pem", + default_upn_domain="example.org", + include_nested_groups=True, + binding=dict( + user_dn="cn=binder,dc=acme,dc=org", + password="YOUR_PASSWORD", + ), + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="group", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="sAMAccountName", + name_attribute="displayName", + object_class="person", + ), + ) + ], + groups_prefix="ad", + username_prefix="ad", + ) + + with pytest.raises(AnsibleExitJson): + ad_auth_provider.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[ + 0 + ] + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory" + assert payload == dict( + type="ad", + api_version="authentication/v2", + metadata=dict(name="activedirectory"), + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=636, + insecure=False, + security="tls", + trusted_ca_file="/path/to/trusted-certificate-authorities.pem", + client_cert_file="/path/to/ssl/cert.pem", + client_key_file="/path/to/ssl/key.pem", + default_upn_domain="example.org", + include_nested_groups=True, + binding=dict( + user_dn="cn=binder,dc=acme,dc=org", + password="YOUR_PASSWORD", + ), + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="group", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="sAMAccountName", + name_attribute="displayName", + object_class="person", + ), + ) + ], + groups_prefix="ad", + username_prefix="ad", + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + ad_auth_provider.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py new file mode 100644 index 000000000..8e9298b2c --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py @@ -0,0 +1,251 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import asset + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + def test_equal_assets_with_none_values(self): + assert asset.do_differ( + { + "name": "asset", + "builds": [ + { + "sha512": "a", + "url": "a", + "filters": None + }, + ], + }, + { + "name": "asset", + "builds": [ + { + "sha512": "a", + "url": "a", + "headers": None, + }, + ], + }, + ) is False + + def test_equal_assets_with_different_build_content(self): + assert asset.do_differ( + { + "name": "asset", + "builds": [ + { + "url": "http://abc.com", + "sha512": "abc", + "headers": { + "foo": "bar", + "bar": "foo", + } + }, + { + "url": "http://def.com", + "sha512": "def", + "filters": ["d == d", "e == e"], + }, + + ] + }, + { + "name": "asset", + "builds": [ + { + "url": "http://def.com", + "sha512": "def", + "filters": ["e == e", "d == d"], + }, + { + "url": "http://abc.com", + "sha512": "abc", + "headers": { + "bar": "foo", + "foo": "bar" + } + }, + ] + }, + ) is False + + def test_updated_asset(self): + assert asset.do_differ( + { + "name": "asset", + "builds": [ + { + "url": "http://abc.com", + "sha512": "abc", + } + ], + "annotations": { + "foo": "bar", + } + }, + { + "name": "asset", + "builds": [ + { + "url": "http://def.com", + "sha512": "abc", + }, + { + "url": "http://def.com", + "sha512": "abc", + "filters": ["abc == def"], + } + ], + }, + ) is True + + def test_different_assets_with_same_builds(self): + assert asset.do_differ( + { + "name": "a", + "builds": [ + { + "url": "http://abc.com", + "sha512": "abc", + } + ], + "annotations": { + "foo": "bar", + } + }, + { + "name": "b", + "builds": [ + { + "url": "http://abc.com", + "sha512": "abc", + } + ], + "annotations": { + "bar": "foo" + } + }, + ) is True + + +class TestAsset(ModuleTestCase): + def test_minimal_asset_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_asset", + builds=[ + dict( + url="http://example.com/asset.tar.gz", + sha512="sha512String", + ), + ] + ) + + with pytest.raises(AnsibleExitJson): + asset.main() + + state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/core/v2/namespaces/default/assets/test_asset" + assert payload == dict( + builds=[ + dict( + url="http://example.com/asset.tar.gz", + sha512="sha512String", + ), + ], + metadata=dict( + name="test_asset", + namespace="default", + ), + ) + assert check_mode is False + + def test_all_asset_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_asset", + namespace="my", + state="present", + builds=[ + dict( + url="http://example.com/asset.tar.gz", + sha512="sha512String", + filters=["a", "b", "c"], + headers={"header": "h"}, + ), + ], + labels={"region": "us-west-1"}, + annotations={"playbook": 12345}, + ) + + with pytest.raises(AnsibleExitJson): + asset.main() + + state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/core/v2/namespaces/my/assets/test_asset" + assert payload == dict( + builds=[ + dict( + url="http://example.com/asset.tar.gz", + sha512="sha512String", + filters=["a", "b", "c"], + headers={"header": "h"}, + ) + ], + metadata=dict( + name="test_asset", + namespace="my", + labels={"region": "us-west-1"}, + annotations={"playbook": "12345"}, + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name="test_asset", + builds=[ + dict( + url="http://example.com/asset.tar.gz", + sha512="sha512String", + ) + ] + + ) + + with pytest.raises(AnsibleFailJson): + asset.main() + + def test_failure_empty_builds(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name="test_asset", + builds=[], + ) + + with pytest.raises(AnsibleFailJson): + asset.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py new file mode 100644 index 000000000..aab09b571 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import asset_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestAssetInfo(ModuleTestCase): + def test_get_all_assets(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + asset_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/assets" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_asset(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-asset") + + with pytest.raises(AnsibleExitJson) as context: + asset_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/assets/sample-asset" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_asset(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-asset") + + with pytest.raises(AnsibleExitJson) as context: + asset_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-asset") + + with pytest.raises(AnsibleFailJson): + asset_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py new file mode 100644 index 000000000..bd19bceb1 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import auth_provider_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestAuthProviderInfo(ModuleTestCase): + def test_get_all_auth_providers(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [dict(spec=dict(a=1)), dict(spec=dict(b=2))] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + auth_provider_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/authentication/v2/authproviders" + assert context.value.args[0]["objects"] == [dict(a=1), dict(b=2)] + + def test_get_single_auth_provider(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = dict(spec=dict(a=1)) + set_module_args(name="sample-auth-provider") + + with pytest.raises(AnsibleExitJson) as context: + auth_provider_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/authentication/v2/authproviders/sample-auth-provider" + assert context.value.args[0]["objects"] == [dict(a=1)] + + def test_missing_single_auth_provider(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-auth-provider") + + with pytest.raises(AnsibleExitJson) as context: + auth_provider_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-auth-provider") + + with pytest.raises(AnsibleFailJson): + auth_provider_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py new file mode 100644 index 000000000..e935a7a9f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import bonsai, errors +from ansible_collections.sensu.sensu_go.plugins.modules import bonsai_asset + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestBonsaiAsset(ModuleTestCase): + def test_success(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + bonsai_params.return_value = dict(sample="value") + + set_module_args(name="name", version="version") + + with pytest.raises(AnsibleExitJson): + bonsai_asset.main() + + def test_bonsai_failure(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + bonsai_params.side_effect = errors.BonsaiError("Bonsai bad") + + set_module_args(name="name", version="version") + + with pytest.raises(AnsibleFailJson): + bonsai_asset.main() + + def test_validation_failure(self, mocker): + bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters") + bonsai_params.return_value = dict(sample="value") + + set_module_args(version="version") + + with pytest.raises(AnsibleFailJson): + bonsai_asset.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py new file mode 100644 index 000000000..8ea6ecb94 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py @@ -0,0 +1,327 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import check + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoSetsDiffer: + @pytest.mark.parametrize("current,desired,diff", [ + ([1, 2, 3], [1, 2, 3], False), + ([1, 2, 3], [3, 2, 1], False), + ([1, 2], [1, 2, 3], True), + ([1, 3], [2, 4], True), + ]) + def test_comparison(self, current, desired, diff): + c = dict(k=current) + d = dict(k=desired) + + assert check.do_sets_differ(c, d, "k") is diff + + def test_missing_keys_are_treated_as_empty_sets(self): + current = dict(a=[]) + desired = dict() + + assert check.do_sets_differ(current, desired, "a") is False + assert check.do_sets_differ(desired, current, "a") is False + + def test_nulls_are_treated_as_empty_sets(self): + current = dict(a=None) + desired = dict(a=[]) + + assert check.do_sets_differ(current, desired, "a") is False + assert check.do_sets_differ(desired, current, "a") is False + + +class TestDoProxyRequestsDiffer: + def test_missing_proxy_requests_in_desired_is_ignored(self): + current = dict(proxy_requests=dict(entity_attributes=["a", "b"])) + desired = dict() + + assert check.do_proxy_requests_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired,diff", [ + (["a", "b"], ["a", "b"], False), + (["a", "b"], ["b", "a"], False), + (None, [], False), + (["a", "b"], ["c", "a"], True), + (["a", "b"], ["a", "b", "c"], True), + ]) + def test_treat_entity_attributes_as_a_set(self, current, desired, diff): + c = dict(proxy_requests=dict(entity_attributes=current)) + d = dict(proxy_requests=dict(entity_attributes=desired)) + + assert check.do_proxy_requests_differ(c, d) is diff + + def test_ignore_missing_entity_attributes_in_desired(self): + current = dict(proxy_requests=dict(entity_attributes=["a", "b"])) + desired = dict(proxy_requests=dict()) + + assert check.do_proxy_requests_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired,diff", [ + (dict(splay=False), dict(splay=False), False), + (dict(splay=False), dict(), False), + (dict(splay=False), dict(splay=True), True), + (dict(), dict(splay=True), True), + ]) + def test_other_stuff_is_compared_as_usual(self, current, desired, diff): + c = dict(proxy_requests=current) + d = dict(proxy_requests=desired) + + assert check.do_proxy_requests_differ(c, d) is diff + + +class TestDoCheckHooksDiffer: + def test_missing_check_hooks_in_desired_is_ignored(self): + current = dict(check_hooks=[dict(warning=["a"])]) + desired = dict() + + assert check.do_check_hooks_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired,diff", [ + (["a", "b"], ["a", "b"], False), + (["a", "b"], ["b", "a"], False), + (["a", "b"], ["c", "a"], True), + (["a", "b"], ["a", "b", "c"], True), + ]) + def test_treat_hooks_as_a_set(self, current, desired, diff): + c = dict(check_hooks=[dict(warning=current)]) + d = dict(check_hooks=[dict(warning=desired)]) + + assert check.do_check_hooks_differ(c, d) is diff + + +class TestDoDiffer: + def test_no_difference(self): + assert not check.do_differ( + dict( + command="sleep", + subscriptions=["sub1", "sub2"], + handlers=["ha1", "ha2", "ha3"], + interval=123, + cron="* * * 3 2", + publish=False, + timeout=30, + ttl=60, + stdin=True, + low_flap_threshold=2, + high_flap_threshold=10, + runtime_assets=["asset1", "asset2"], + check_hooks=[ + dict(warning=["hook0-1", "hook0-2"]), + dict(critical=["hook1-1", "hook1-2"]), + ], + proxy_entity_name="name", + proxy_requests=dict( + entity_attributes=["a1", "a2", "a3"], + splay=True, + splay_coverage=10, + ), + output_metric_format="influxdb_line", + output_metric_handlers=["mhandler1", "mhandler2"], + round_robin=False, + env_vars=["k1=v1", "k2=v2"], + ), + dict( + command="sleep", + subscriptions=["sub2", "sub1"], + handlers=["ha3", "ha1", "ha2"], + interval=123, + cron="* * * 3 2", + publish=False, + timeout=30, + ttl=60, + stdin=True, + low_flap_threshold=2, + high_flap_threshold=10, + runtime_assets=["asset2", "asset1"], + check_hooks=[ + dict(critical=["hook1-2", "hook1-1"]), + dict(warning=["hook0-2", "hook0-1"]), + ], + proxy_entity_name="name", + proxy_requests=dict( + splay=True, + entity_attributes=["a3", "a2", "a1"], + splay_coverage=10, + ), + output_metric_format="influxdb_line", + output_metric_handlers=["mhandler2", "mhandler1"], + round_robin=False, + env_vars=["k2=v2", "k1=v1"], + ) + ) + + @pytest.mark.parametrize("current,desired", [ + ( # No diff in params, no secrets + dict(name="demo"), + dict(name="demo"), + ), + ( # No diff in params, no diff in secrets + dict(name="demo", secrets=[ + dict(name="n1", secret="s1"), dict(name="n2", secret="s2"), + ]), + dict(name="demo", secrets=[ + dict(name="n2", secret="s2"), dict(name="n1", secret="s1"), + ]), + ), + ]) + def test_no_difference_secrets(self, current, desired): + assert check.do_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired", [ + ( # Diff in params, no diff in secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="a", secret="1")]), + ), + ( # No diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="demo", secrets=[dict(name="b", secret="2")]), + ), + ( # Diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="b", secret="2")]), + ), + ]) + def test_difference_secrets(self, current, desired): + assert check.do_differ(current, desired) is True + + +class TestSensuGoCheck(ModuleTestCase): + def test_minimal_check_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_check", + command='echo "test"', + subscriptions=['switches'], + interval=60 + ) + + with pytest.raises(AnsibleExitJson): + check.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/core/v2/namespaces/default/checks/test_check" + assert payload == dict( + command='echo "test"', + subscriptions=['switches'], + interval=60, + metadata=dict( + name="test_check", + namespace="default", + ), + ) + assert check_mode is False + + def test_all_check_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name='test_check', + namespace='my', + state='absent', + command='/bin/true', + subscriptions=['checks', 'also_checks'], + handlers=['default', 'not_default'], + interval=30, + publish=True, + timeout=30, + ttl=100, + stdin=False, + low_flap_threshold=20, + high_flap_threshold=60, + proxy_entity_name='switch-dc-01', + proxy_requests=dict( + entity_attributes=['entity.entity_class == "proxy"'], + splay=True, + splay_coverage=90 + ), + output_metric_format='nagios_perfdata', + output_metric_handlers=['influx-db'], + round_robin=True, + env_vars=dict(foo='bar'), + runtime_assets='awesomeness', + secrets=[dict(name="a", secret="b")], + ) + + with pytest.raises(AnsibleExitJson): + check.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == "absent" + assert path == "/api/core/v2/namespaces/my/checks/test_check" + assert payload == dict( + command='/bin/true', + subscriptions=['checks', 'also_checks'], + interval=30, + timeout=30, + publish=True, + handlers=['default', 'not_default'], + env_vars=['foo=bar'], + output_metric_handlers=['influx-db'], + ttl=100, + output_metric_format='nagios_perfdata', + proxy_entity_name='switch-dc-01', + proxy_requests=dict(entity_attributes=['entity.entity_class == "proxy"'], + splay=True, + splay_coverage=90), + high_flap_threshold=60, + low_flap_threshold=20, + round_robin=True, + stdin=False, + runtime_assets=['awesomeness'], + metadata=dict( + name="test_check", + namespace="my", + ), + secrets=[dict(name="a", secret="b")], + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_check', + command='/bin/true', + subscriptions=['checks', 'also_checks'], + handlers=['default', 'not_default'], + interval=30, + publish=True, + timeout=30, + ttl=100, + stdin=False, + low_flap_threshold=20, + high_flap_threshold=60, + proxy_entity_name='switch-dc-01', + proxy_requests=dict( + entity_attributes=['entity.entity_class == "proxy"'], + splay=True, + splay_coverage=90 + ), + output_metric_format='nagios_perfdata', + output_metric_handlers=['influx-db'], + round_robin=True, + env_vars=dict(foo='bar'), + runtime_assets='awesomeness' + ) + + with pytest.raises(AnsibleFailJson): + check.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py new file mode 100644 index 000000000..53ed5b7c6 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import check_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSensuGoCheckInfo(ModuleTestCase): + def test_get_all_checks(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + check_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/checks" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_check(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-check") + + with pytest.raises(AnsibleExitJson) as context: + check_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/checks/sample-check" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_check(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-check") + + with pytest.raises(AnsibleExitJson) as context: + check_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-check") + + with pytest.raises(AnsibleFailJson): + check_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py new file mode 100644 index 000000000..289933644 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py @@ -0,0 +1,83 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestCluster(ModuleTestCase): + @pytest.mark.parametrize("params", [ + {"name": "demo", "state": "absent"}, + {"name": "demo", "api_urls": "url"}, + ]) + def test_minimal_parameters(self, mocker, params): + mocker.patch.object(utils, "sync_v1").return_value = True, {} + set_module_args(**params) + + with pytest.raises(AnsibleExitJson): + cluster.main() + + def test_all_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.return_value = True, {} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + state="present", + name="demo", + api_urls=["a", "b"], + ) + + with pytest.raises(AnsibleExitJson): + cluster.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/enterprise/federation/v1/clusters/demo" + assert payload == dict( + type="Cluster", + api_version="federation/v1", + metadata=dict(name="demo"), + spec=dict(api_urls=["a", "b"]), + ) + assert check_mode is False + + @pytest.mark.parametrize("skip", ["api_urls"]) + def test_missing_required_param_present(self, mocker, skip): + sync_mock = mocker.patch.object(utils, "sync_v1") + all_args = dict(name="demo", api_urls="url") + set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip)) + + with pytest.raises(AnsibleFailJson): + cluster.main() + + sync_mock.assert_not_called() + + def test_failure(self, mocker): + mocker.patch.object(utils, "sync_v1").side_effect = ( + errors.Error("Bad error") + ) + set_module_args(name="demo", state="absent") + + with pytest.raises(AnsibleFailJson): + cluster.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py new file mode 100644 index 000000000..45158dcd3 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestClusterInfo(ModuleTestCase): + def test_all_parameters(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k1": "v1"}} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + name="demo", + ) + + with pytest.raises(AnsibleExitJson): + cluster_info.main() + + def test_get_all_clusters(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [ + {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}}, + ] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + cluster_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/federation/v1/clusters" + assert context.value.args[0]["objects"] == [ + {"k1": "v1"}, {"k2": "v2"}, + ] + + def test_get_single_cluster(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k3": "v3"}} + set_module_args(name="demo") + + with pytest.raises(AnsibleExitJson) as context: + cluster_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/federation/v1/clusters/demo" + assert context.value.args[0]["objects"] == [{"k3": "v3"}] + + def test_missing_single_cluster(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="demo") + + with pytest.raises(AnsibleExitJson) as context: + cluster_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="demo") + + with pytest.raises(AnsibleFailJson): + cluster_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py new file mode 100644 index 000000000..48e939a03 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py @@ -0,0 +1,128 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestClusterRole(ModuleTestCase): + def test_minimal_cluster_role_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_cluster_role', + rules=[ + dict( + verbs=[], + resources=[] + ), + ] + ) + + with pytest.raises(AnsibleExitJson): + cluster_role.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/clusterroles/test_cluster_role' + assert payload == dict( + rules=[ + dict( + verbs=[], + resources=[], + resource_names=None, + ) + ], + metadata=dict( + name='test_cluster_role', + ), + ) + assert check_mode is False + + def test_all_cluster_role_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_cluster_role', + state='present', + rules=[ + dict( + verbs=['get', 'list', 'create'], + resources=['assets', 'entities'], + ), + dict( + verbs=['list'], + resources=['check'], + resource_names=['my-check'], + ) + ], + ) + + with pytest.raises(AnsibleExitJson): + cluster_role.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/clusterroles/test_cluster_role' + assert payload == dict( + metadata=dict( + name='test_cluster_role', + ), + rules=[ + dict( + verbs=['get', 'list', 'create'], + resources=['assets', 'entities'], + resource_names=None, + ), + dict( + verbs=['list'], + resources=['check'], + resource_names=['my-check'], + ) + ], + ) + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_cluster_role', + rules=[ + dict( + verbs=[], + resources=[], + ) + ], + ) + with pytest.raises(AnsibleFailJson): + cluster_role.main() + + def test_failure_invalid_verb(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_cluster_role', + rules=[ + dict( + verbs=['list', 'invalid'], + resources=[], + ), + ] + ) + + with pytest.raises(AnsibleFailJson): + cluster_role.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py new file mode 100644 index 000000000..b18c2d226 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py @@ -0,0 +1,152 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestClusterRoleBinding(ModuleTestCase): + def test_minimal_cluster_role_binding_parameters_users(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_cluster_role_binding', + cluster_role='test_cluster_role', + users=['test_user'], + ) + + with pytest.raises(AnsibleExitJson): + cluster_role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding' + assert payload == dict( + role_ref=dict( + name='test_cluster_role', + type='ClusterRole', + ), + subjects=[ + dict( + name='test_user', + type='User', + ), + ], + metadata=dict( + name='test_cluster_role_binding', + ), + ) + assert check_mode is False + + def test_minimal_cluster_role_binding_parameters_groups(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_cluster_role_binding', + cluster_role='test_cluster_role', + groups=['test_group'], + ) + + with pytest.raises(AnsibleExitJson): + cluster_role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding' + assert payload == dict( + role_ref=dict( + name='test_cluster_role', + type='ClusterRole', + ), + subjects=[ + dict( + name='test_group', + type='Group', + ), + ], + metadata=dict( + name='test_cluster_role_binding', + ), + ) + assert check_mode is False + + def test_all_cluster_role_binding_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_cluster_role_binding', + cluster_role='test_cluster_role', + users=['user_1', 'user_2'], + groups=['group_1', 'group_2'], + ) + + with pytest.raises(AnsibleExitJson): + cluster_role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding' + assert payload == dict( + metadata=dict( + name='test_cluster_role_binding', + ), + role_ref=dict( + name='test_cluster_role', + type='ClusterRole', + ), + subjects=[ + dict( + name='group_1', + type='Group', + ), + dict( + name='group_2', + type='Group', + ), + dict( + name='user_1', + type='User', + ), + dict( + name='user_2', + type='User', + ), + ] + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_cluster_role_binding', + cluster_role='test_cluster_role', + users=['test_user'], + ) + with pytest.raises(AnsibleFailJson): + cluster_role_binding.main() + + def test_failure_missing_groups_or_users(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_cluster_role_binding', + cluster_role='test_cluster_role', + ) + + with pytest.raises(AnsibleFailJson): + cluster_role_binding.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py new file mode 100644 index 000000000..34e58451f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestClusterRoleBindingInfo(ModuleTestCase): + def test_get_all_cluster_role_bindings(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_binding_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/clusterrolebindings" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_cluster_role_binding(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 1 + set_module_args(name="test-cluster-role-binding") + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_binding_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/clusterrolebindings/test-cluster-role-binding" + assert context.value.args[0]["objects"] == [1] + + def test_missing_single_cluster_role_binding(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-cluster-role-binding") + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_binding_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-cluster-role-binding") + + with pytest.raises(AnsibleFailJson): + cluster_role_binding_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py new file mode 100644 index 000000000..11eadb6a6 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestClusterRoleInfo(ModuleTestCase): + def test_get_all_cluster_roles(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/clusterroles" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_cluster_role(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 1 + set_module_args(name="test-cluster-role") + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/clusterroles/test-cluster-role" + assert context.value.args[0]["objects"] == [1] + + def test_missing_single_cluster_role(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-cluster-role") + + with pytest.raises(AnsibleExitJson) as context: + cluster_role_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-cluster-role") + + with pytest.raises(AnsibleFailJson): + cluster_role_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py new file mode 100644 index 000000000..665b29ab8 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py @@ -0,0 +1,269 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, http, +) +from ansible_collections.sensu.sensu_go.plugins.modules import datastore + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSync(ModuleTestCase): + def test_absent_no_current_object(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + changed, object = datastore.sync( + "absent", client, "/list", "/resource", {}, False, + ) + + assert changed is False + assert object is None + + def test_absent_no_current_object_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, "") + + changed, object = datastore.sync( + "absent", client, "/list", "/resource", {}, True, + ) + + assert changed is False + assert object is None + + def test_absent_current_object_present(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + client.delete.return_value = http.Response(204, "") + + changed, object = datastore.sync( + "absent", client, "/list", "/resource", {}, False, + ) + + assert changed is True + assert object is None + client.delete.assert_called_with("/resource") + + def test_absent_current_object_present_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + client.delete.return_value = http.Response(204, "") + + changed, object = datastore.sync( + "absent", client, "/list", "/resource", {}, True, + ) + + assert changed is True + assert object is None + client.delete.assert_not_called() + + def test_present_current_object_differ(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(200, '{"spec": {"current": "data"}}'), + http.Response(200, '{"spec": {"new": "data"}}'), + ) + client.put.return_value = http.Response(201, "") + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + False, + ) + + assert changed is True + assert {"new": "data"} == object + client.put.assert_called_once_with( + "/resource", {"spec": {"my": "data"}}, + ) + + def test_present_current_object_differ_check(self, mocker): + client = mocker.Mock() + client.get.return_value = ( + http.Response(200, '{"spec": {"current": "data"}}') + ) + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + True, + ) + + assert changed is True + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_current_object_does_not_differ(self, mocker): + client = mocker.Mock() + client.get.return_value = ( + http.Response(200, '{"spec": {"my": "data"}}') + ) + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + False, + ) + + assert changed is False + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_current_object_does_not_differ_check(self, mocker): + client = mocker.Mock() + client.get.return_value = ( + http.Response(200, '{"spec": {"my": "data"}}') + ) + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + True, + ) + + assert changed is False + assert {"my": "data"} == object + client.put.assert_not_called() + + def test_present_no_current_object_empty_backend(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(404, ""), + http.Response(200, "[]"), + http.Response(200, '{"spec": {"new": "data"}}'), + ) + client.put.return_value = http.Response(201, "") + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + False, + ) + + assert changed is True + assert {"new": "data"} == object + client.put.assert_called_once_with( + "/resource", {"spec": {"my": "data"}}, + ) + + def test_present_no_current_object_empty_backend_check(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(404, ""), + http.Response(200, "[]"), + ) + + changed, object = datastore.sync( + "present", client, "/list", "/resource", {"spec": {"my": "data"}}, + True, + ) + + assert changed is True + assert {"my": "data"} == object + client.put.assert_not_called() + + @pytest.mark.parametrize("check", [False, True]) + def test_present_no_current_object_non_empty_backend(self, mocker, check): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(404, ""), + http.Response(200, "[{}]"), + ) + + with pytest.raises(errors.Error, match="already active"): + datastore.sync( + "present", client, "/list", "/resource", + {"spec": {"my": "data"}}, check, + ) + + client.put.assert_not_called() + + +class TestDatastore(ModuleTestCase): + def test_minimal_datastore_parameters_present(self, mocker): + sync_mock = mocker.patch.object(datastore, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_datastore", + dsn="my-dsn", + ) + + with pytest.raises(AnsibleExitJson): + datastore.main() + + state, _client, list_path, resource_path, payload, check_mode = ( + sync_mock.call_args[0] + ) + assert state == "present" + assert resource_path == "/api/enterprise/store/v1/provider/test_datastore" + assert list_path == "/api/enterprise/store/v1/provider" + assert payload == dict( + type="PostgresConfig", + api_version="store/v1", + metadata=dict(name="test_datastore"), + spec=dict(dsn="my-dsn"), + ) + assert check_mode is False + + def test_minimal_datastore_parameters_absent(self, mocker): + sync_mock = mocker.patch.object(datastore, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_datastore", + state="absent", + ) + + with pytest.raises(AnsibleExitJson): + datastore.main() + + state, _client, list_path, resource_path, _payload, check_mode = ( + sync_mock.call_args[0] + ) + assert state == "absent" + assert resource_path == "/api/enterprise/store/v1/provider/test_datastore" + assert list_path == "/api/enterprise/store/v1/provider" + assert check_mode is False + + def test_all_datastore_parameters(self, mocker): + sync_mock = mocker.patch.object(datastore, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_datastore", + dsn="my-dsn", + pool_size=543, + ) + + with pytest.raises(AnsibleExitJson): + datastore.main() + + state, _client, list_path, resource_path, payload, check_mode = ( + sync_mock.call_args[0] + ) + assert state == "present" + assert resource_path == "/api/enterprise/store/v1/provider/test_datastore" + assert list_path == "/api/enterprise/store/v1/provider" + assert payload == dict( + type="PostgresConfig", + api_version="store/v1", + metadata=dict(name="test_datastore"), + spec=dict(dsn="my-dsn", pool_size=543), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(datastore, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name="test_datastore", + dsn="my-dsn", + ) + + with pytest.raises(AnsibleFailJson): + datastore.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py new file mode 100644 index 000000000..4a1efc9c2 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import datastore_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDatastoreInfo(ModuleTestCase): + def test_get_all_datastores(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [dict(spec=1), dict(spec=2)] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + datastore_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/store/v1/provider" + assert context.value.args[0]["objects"] == [1, 2] + + def test_get_single_datastore(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = dict(spec=4) + set_module_args(name="sample-datastore") + + with pytest.raises(AnsibleExitJson) as context: + datastore_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/store/v1/provider/sample-datastore" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_datastore(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-datastore") + + with pytest.raises(AnsibleExitJson) as context: + datastore_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-datastore") + + with pytest.raises(AnsibleFailJson): + datastore_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py new file mode 100644 index 000000000..fb098ed87 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py @@ -0,0 +1,199 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import entity + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + @pytest.mark.parametrize('current', [ + dict(no=dict(system="here")), + dict(system=dict(here="is")), + ]) + def test_no_system_in_desired(self, current): + assert entity.do_differ(current, {}) is False + + def test_system_keys_not_in_current_are_ignored(self): + assert entity.do_differ( + dict(system=dict(a=1, b=2)), + dict(system=dict(a=1)), + ) is False + + def test_actual_changes_are_detected(self): + assert entity.do_differ( + dict(system=dict(a=1, b=2)), + dict(system=dict(a=2)), + ) is True + + def test_missing_keys_are_detected(self): + assert entity.do_differ( + dict(system=dict(b=2)), + dict(system=dict(a=2)), + ) is True + + @pytest.mark.parametrize("current,desired", [ + ([], None), ([], []), + (["a"], ["a"]), + (["a", "b"], ["b", "a"]), + ]) + def test_no_diff_in_subscriptions(self, current, desired): + assert entity.do_differ( + dict(subscriptions=current), dict(subscriptions=desired), + ) is False + + @pytest.mark.parametrize("current,desired", [ + ([], ["a"]), (["a"], []), + (["a"], ["b"]), + (["a", "b"], ["a", "c"]), + ]) + def test_diff_in_subscriptions(self, current, desired): + print((current, desired)) + assert entity.do_differ( + dict(subscriptions=current), dict(subscriptions=desired), + ) is True + + +class TestEntity(ModuleTestCase): + def test_minimal_entity_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_entity', + entity_class='proxy', + ) + + with pytest.raises(AnsibleExitJson): + entity.main() + + state, _c, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/entities/test_entity' + assert payload == dict( + entity_class='proxy', + metadata=dict( + name='test_entity', + namespace='default', + ), + ) + assert check_mode is False + + def test_minimal_entity_parameters_agent_class(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_entity', + entity_class='agent', + ) + + with pytest.raises(AnsibleExitJson): + entity.main() + + state, _c, path, payload, check_mode, _d = sync_mock.call_args[0] + print(payload) + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/entities/test_entity' + assert payload == dict( + entity_class='agent', + metadata=dict( + name='test_entity', + namespace='default', + ), + subscriptions=['entity:test_entity'], + ) + assert check_mode is False + + def test_all_entity_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_entity', + namespace='my', + state='absent', + entity_class='proxy', + subscriptions=['web', 'prod'], + system=dict( + hostname='test-entity', + os='linux', + platform='ubuntu', + network=dict( + interfaces=[ + dict( + name='lo', + addresses=['127.0.0.1/8', '::1/128'] + ), + dict( + name='eth0', + mac='52:54:00:20:1b:3c', + addresses=['93.184.216.34/24'] + ) + ]) + ), + last_seen=1522798317, + deregister=True, + deregistration_handler='email-handler', + redact=['password', 'pass', 'api_key'], + user='agent' + ) + + with pytest.raises(AnsibleExitJson): + entity.main() + + state, _c, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == 'absent' + assert path == '/api/core/v2/namespaces/my/entities/test_entity' + assert payload == dict( + entity_class='proxy', + subscriptions=['web', 'prod'], + system=dict( + hostname='test-entity', + os='linux', + platform='ubuntu', + network=dict( + interfaces=[ + dict( + name='lo', + addresses=['127.0.0.1/8', '::1/128'] + ), + dict( + name='eth0', + mac='52:54:00:20:1b:3c', + addresses=['93.184.216.34/24'] + ) + ]) + ), + last_seen=1522798317, + deregister=True, + deregistration=dict(handler='email-handler'), + redact=['password', 'pass', 'api_key'], + user='agent', + metadata=dict( + name='test_entity', + namespace='my' + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_entity', + entity_class='proxy' + ) + + with pytest.raises(AnsibleFailJson): + entity.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py new file mode 100644 index 000000000..964dcaed0 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import entity_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestEntityInfo(ModuleTestCase): + def test_get_all_entities(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + entity_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/entities" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_entity(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-entity") + + with pytest.raises(AnsibleExitJson) as context: + entity_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/entities/sample-entity" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_entity(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-entity") + + with pytest.raises(AnsibleExitJson) as context: + entity_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-entity") + + with pytest.raises(AnsibleFailJson): + entity_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py new file mode 100644 index 000000000..982c97c57 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py @@ -0,0 +1,132 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestEtcdReplicator(ModuleTestCase): + @pytest.mark.parametrize("params", [ + {"name": "demo", "state": "absent"}, + {"name": "demo", "insecure": True, "url": "url", "resource": "resource"}, + { + "name": "demo", + "url": "url", + "resource": "resource", + "ca_cert": "ca_cert", + "cert": "cert", + "key": "key", + }, + ]) + def test_minimal_parameters(self, mocker, params): + mocker.patch.object(utils, "sync_v1").return_value = True, {} + set_module_args(**params) + + with pytest.raises(AnsibleExitJson): + etcd_replicator.main() + + def test_all_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.return_value = True, {} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + state="present", + name="demo", + ca_cert="ca_cert", + cert="cert", + key="key", + insecure=True, + url=["a", "b"], + api_version="api_version", + resource="resource", + namespace="namespace", + replication_interval=30, + ) + + with pytest.raises(AnsibleExitJson): + etcd_replicator.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/enterprise/federation/v1/etcd-replicators/demo" + assert payload == dict( + type="EtcdReplicator", + api_version="federation/v1", + metadata=dict(name="demo"), + spec=dict( + ca_cert="ca_cert", + cert="cert", + key="key", + insecure=True, + url="a,b", + api_version="api_version", + resource="resource", + namespace="namespace", + replication_interval_seconds=30, + ), + ) + assert check_mode is False + + @pytest.mark.parametrize("skip", ["ca_cert", "cert", "key", "url", "resource"]) + def test_missing_required_param_present_secure(self, mocker, skip): + sync_mock = mocker.patch.object(utils, "sync_v1") + all_args = dict( + name="demo", + ca_cert="ca_cert", + cert="cert", + key="key", + url="url", + resource="resource", + ) + set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip)) + + with pytest.raises(AnsibleFailJson): + etcd_replicator.main() + + sync_mock.assert_not_called() + + @pytest.mark.parametrize("skip", ["url", "resource"]) + def test_missing_required_param_present_insecure(self, mocker, skip): + sync_mock = mocker.patch.object(utils, "sync_v1") + all_args = dict( + name="demo", + insecure=True, + url="url", + resource="resource", + ) + set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip)) + + with pytest.raises(AnsibleFailJson): + etcd_replicator.main() + + sync_mock.assert_not_called() + + def test_failure(self, mocker): + mocker.patch.object(utils, "sync_v1").side_effect = ( + errors.Error("Bad error") + ) + set_module_args(name="demo", state="absent") + + with pytest.raises(AnsibleFailJson): + etcd_replicator.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py new file mode 100644 index 000000000..e651dab76 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestEtcdReplicatorInfo(ModuleTestCase): + def test_all_parameters(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k1": "v1"}} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + name="demo", + ) + + with pytest.raises(AnsibleExitJson): + etcd_replicator_info.main() + + def test_get_all_secrets(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [ + {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}}, + ] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + etcd_replicator_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/federation/v1/etcd-replicators" + assert context.value.args[0]["objects"] == [ + {"k1": "v1"}, {"k2": "v2"}, + ] + + def test_get_single_replicator(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k3": "v3"}} + set_module_args(name="demo") + + with pytest.raises(AnsibleExitJson) as context: + etcd_replicator_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/federation/v1/etcd-replicators/demo" + assert context.value.args[0]["objects"] == [{"k3": "v3"}] + + def test_missing_single_replicator(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="demo") + + with pytest.raises(AnsibleExitJson) as context: + etcd_replicator_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="demo") + + with pytest.raises(AnsibleFailJson): + etcd_replicator_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py new file mode 100644 index 000000000..8808ec2c5 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py @@ -0,0 +1,286 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, http, +) +from ansible_collections.sensu.sensu_go.plugins.modules import event + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestGetObjects: + def test_get_entity(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"entity": "entity"}') + resp = event.get_entity(client, 'default', 'entity') + + assert resp == {'entity': 'entity'} + + def test_get_entity_404(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, '') + + with pytest.raises(errors.SyncError, + match="Entity with name 'entity' does not exist on remote."): + event.get_entity(client, 'default', 'entity') + + def test_get_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"check": "check"}') + resp = event.get_check(client, 'default', 'check') + + assert resp == {'check': 'check'} + + def test_get_check_404(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(404, '') + + with pytest.raises(errors.SyncError, + match="Check with name 'check' does not exist on remote."): + event.get_check(client, 'default', 'check') + + +class TestEvent(ModuleTestCase): + def test_missing_entity_on_remote(self, mocker): + get_entity_mock = mocker.patch.object(event, 'get_entity') + get_entity_mock.side_effect = errors.SyncError('Error') + + set_module_args( + entity='awesome_entity', + check='awesome_check', + ) + + with pytest.raises(AnsibleFailJson, match='Error'): + event.main() + + def test_missing_check_on_remote(self, mocker): + mocker.patch.object(event, 'get_entity') + get_check_mock = mocker.patch.object(event, 'get_check') + get_check_mock.side_effect = errors.SyncError('Error') + + set_module_args( + entity='awesome_entity', + check='awesome_check', + ) + + with pytest.raises(AnsibleFailJson, match='Error'): + event.main() + + def test_minimal_event_parameters(self, mocker): + send_event_mock = mocker.patch.object(event, 'send_event') + send_event_mock.return_value = True, {} + get_entity_mock = mocker.patch.object(event, 'get_entity') + get_entity_mock.return_value = dict( + metadata=dict( + name='awesome_entity', + namespace='default' + ), + entity_class='proxy' + ) + get_check_mock = mocker.patch.object(event, 'get_check') + get_check_mock.return_value = dict( + metadata=dict( + name='awesome_check', + namespace='default' + ) + ) + + set_module_args( + entity='awesome_entity', + check='awesome_check', + ) + + with pytest.raises(AnsibleExitJson): + event.main() + + _client, path, payload, check_mode = send_event_mock.call_args[0] + assert path == '/api/core/v2/namespaces/default/events/awesome_entity/awesome_check' + assert payload == dict( + metadata=dict( + namespace='default' + ), + entity=dict( + metadata=dict( + name='awesome_entity', + namespace='default' + ), + entity_class='proxy' + ), + check=dict( + metadata=dict( + name='awesome_check', + namespace='default' + ) + ) + ) + assert check_mode is False + + def test_all_event_parameters(self, mocker): + entity_object = dict( + metadata=dict( + name='awesome_entity', + namespace='default' + ), + entity_class='proxy' + ) + check_object = dict( + metadata=dict( + name='awesome_check', + namespace='default' + ), + command="check-cpu.sh -w 75 -c 90", + handlers=["slack"], + interval=60, + publish=True, + subscriptions=["linux"], + ) + send_event_mock = mocker.patch.object(event, 'send_event') + send_event_mock.return_value = True, {} + get_entity_mock = mocker.patch.object(event, 'get_entity') + get_entity_mock.return_value = entity_object + get_check_mock = mocker.patch.object(event, 'get_check') + get_check_mock.return_value = check_object + + set_module_args( + namespace='my', + timestamp=1234567, + entity='awesome_entity', + check='awesome_check', + check_attributes=dict( + duration=1.945, + executed=1522100915, + history=[ + dict( + executed=1552505193, + status=1 + ), + dict( + executed=1552505293, + status=0 + ), + dict( + executed=1552505393, + status=0 + ), + dict( + executed=1552505493, + status=0 + ) + ], + issued=1552506033, + last_ok=1552506033, + output='10', + state='passing', + status='ok', + total_state_change=0 + ), + metric_attributes=dict( + handlers=['handler1', 'handler2'], + points=[{ + 'name': 'sensu-go-sandbox.curl_timings.time_total', + 'tags': [], + 'timestamp': 1552506033, + 'value': 0.005 + }, { + 'name': 'sensu-go-sandbox.curl_timings.time_namelookup', + 'tags': [], + 'timestamp': 1552506033, + 'value': 0.004 + }] + ) + ) + + with pytest.raises(AnsibleExitJson): + event.main() + + _client, path, payload, check_mode = send_event_mock.call_args[0] + assert path == '/api/core/v2/namespaces/my/events/awesome_entity/awesome_check' + assert payload == dict( + metadata=dict( + namespace='my' + ), + timestamp=1234567, + entity=dict( + metadata=dict( + name='awesome_entity', + namespace='default' + ), + entity_class='proxy' + ), + check=dict( + metadata=dict( + name='awesome_check', + namespace='default' + ), + command="check-cpu.sh -w 75 -c 90", + handlers=["slack"], + interval=60, + publish=True, + subscriptions=["linux"], + duration=1.945, + executed=1522100915, + history=[ + dict( + executed=1552505193, + status=1 + ), + dict( + executed=1552505293, + status=0 + ), + dict( + executed=1552505393, + status=0 + ), + dict( + executed=1552505493, + status=0 + ) + ], + issued=1552506033, + last_ok=1552506033, + output='10', + state='passing', + status=0, + total_state_change=0 + ), + metrics=dict( + handlers=['handler1', 'handler2'], + points=[{ + 'name': 'sensu-go-sandbox.curl_timings.time_total', + 'tags': [], + 'timestamp': 1552506033, + 'value': 0.005 + }, { + 'name': 'sensu-go-sandbox.curl_timings.time_namelookup', + 'tags': [], + 'timestamp': 1552506033, + 'value': 0.004 + }] + ) + ) + assert check_mode is False + + def test_failure(self, mocker): + get_entity_mock = mocker.patch.object(event, 'get_entity') + get_entity_mock.side_effect = errors.Error('Bad error') + set_module_args( + entity='awesome_entity', + check=dict( + name='awesome_check' + ) + ) + + with pytest.raises(AnsibleFailJson): + event.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py new file mode 100644 index 000000000..c9270892d --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import event_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestEventInfo(ModuleTestCase): + def test_get_all_events(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + event_info.main() + + _client, path = get_mock.call_args[0] + assert path == '/api/core/v2/namespaces/my/events' + assert context.value.args[0]['objects'] == [1, 2, 3] + + def test_get_events_by_entity(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = [1, 2] + set_module_args( + entity='simple-entity' + ) + + with pytest.raises(AnsibleExitJson) as context: + event_info.main() + + _client, path = get_mock.call_args[0] + assert path == '/api/core/v2/namespaces/default/events/simple-entity' + assert context.value.args[0]['objects'] == [1, 2] + + def test_get_events_by_check(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = [1, 2] + set_module_args( + check='simple-check' + ) + + with pytest.raises(AnsibleFailJson, + match=r"missing parameter\(s\) required by 'check': entity"): + event_info.main() + + def test_get_single_event_by_entity_and_check(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = 4 + set_module_args( + entity='simple-entity', + check='simple-check' + ) + + with pytest.raises(AnsibleExitJson) as context: + event_info.main() + + _client, path = get_mock.call_args[0] + assert path == '/api/core/v2/namespaces/default/events/simple-entity/simple-check' + assert context.value.args[0]['objects'] == [4] + + def test_no_event_by_entity_and_check(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args( + entity='simple-entity', + check='simple-check' + ) + + with pytest.raises(AnsibleExitJson) as context: + event_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.side_effect = errors.Error('Bad error') + set_module_args(entity='simple-entity') + + with pytest.raises(AnsibleFailJson): + event_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py new file mode 100644 index 000000000..f92a52623 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py @@ -0,0 +1,91 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import filter + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestFilter(ModuleTestCase): + def test_minimal_filter_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_filter', + action='allow', + expressions='event.check.occurences == 1', + ) + + with pytest.raises(AnsibleExitJson): + filter.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/filters/test_filter' + assert payload == dict( + action='allow', + expressions=['event.check.occurences == 1'], + metadata=dict( + name='test_filter', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_filter_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_filter', + namespace='my', + state='absent', + action='allow', + expressions='event.check.occurences == 1', + runtime_assets='awesomeness', + labels={'region': 'us-west-1'}, + annotations={'playbook': 12345}, + ) + + with pytest.raises(AnsibleExitJson): + filter.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'absent' + assert path == '/api/core/v2/namespaces/my/filters/test_filter' + assert payload == dict( + action='allow', + expressions=['event.check.occurences == 1'], + runtime_assets=['awesomeness'], + metadata=dict( + name='test_filter', + namespace='my', + labels={'region': 'us-west-1'}, + annotations={'playbook': '12345'}, + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_filter', + action='deny', + expressions='event.check.occurences == 1', + ) + + with pytest.raises(AnsibleFailJson): + filter.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py new file mode 100644 index 000000000..d8bbcf41a --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import filter_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestFilterInfo(ModuleTestCase): + def test_get_all_filters(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + filter_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/filters" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_filter(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-filter") + + with pytest.raises(AnsibleExitJson) as context: + filter_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/filters/sample-filter" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_filter(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-filter") + + with pytest.raises(AnsibleExitJson) as context: + filter_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-filter") + + with pytest.raises(AnsibleFailJson): + filter_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py new file mode 100644 index 000000000..bd6aa8059 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py @@ -0,0 +1,59 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import handler_set + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestHandlerSet(ModuleTestCase): + def test_all_handler_set_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name='test_handler', + namespace='my', + state='absent', + handlers=['tcp_handler', 'udp_handler'] + ) + + with pytest.raises(AnsibleExitJson): + handler_set.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "absent" + assert path == "/api/core/v2/namespaces/my/handlers/test_handler" + assert payload == dict( + type='set', + handlers=['tcp_handler', 'udp_handler'], + metadata=dict( + name="test_handler", + namespace="my", + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_handler', + state='absent', + handlers=['tcp_handler', 'udp_handler'] + ) + + with pytest.raises(AnsibleFailJson): + handler_set.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py new file mode 100644 index 000000000..3b6e6da8d --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import hook + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestHook(ModuleTestCase): + def test_minimal_hook_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_hook', + command='/bin/true', + timeout=10, + ) + + with pytest.raises(AnsibleExitJson): + hook.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/hooks/test_hook' + assert payload == dict( + command='/bin/true', + timeout=10, + metadata=dict( + name='test_hook', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_hook_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_hook', + namespace='my', + state='absent', + command='/bin/true', + timeout=30, + stdin=True, + runtime_assets='awesomeness', + labels={'region': 'us-west-1'}, + annotations={'playbook': 12345}, + ) + + with pytest.raises(AnsibleExitJson): + hook.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'absent' + assert path == '/api/core/v2/namespaces/my/hooks/test_hook' + assert payload == dict( + command='/bin/true', + timeout=30, + stdin=True, + runtime_assets=['awesomeness'], + metadata=dict( + name='test_hook', + namespace='my', + labels={'region': 'us-west-1'}, + annotations={'playbook': '12345'}, + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_hook', + command='/bin/true', + timeout=10 + ) + + with pytest.raises(AnsibleFailJson): + hook.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py new file mode 100644 index 000000000..82269840f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import hook_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestHookInfo(ModuleTestCase): + def test_get_all_hooks(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + hook_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/hooks" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_hook(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-hook") + + with pytest.raises(AnsibleExitJson) as context: + hook_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/hooks/sample-hook" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_hook(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-hook") + + with pytest.raises(AnsibleExitJson) as context: + hook_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-hook") + + with pytest.raises(AnsibleFailJson): + hook_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py new file mode 100644 index 000000000..4b9e1c463 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py @@ -0,0 +1,326 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, + utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import ldap_auth_provider + +from .common.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + def test_no_changes(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict(name="openldap"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="openldap", + created_by="me", + ), + ) + + assert ldap_auth_provider.do_differ(current, desired) is False + + def test_changes_are_detected(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=636, + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict(name="openldap"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="openldap", + created_by="me", + ), + ) + assert ldap_auth_provider.do_differ(current, desired) is True + + def test_changes_are_detected_diff_servers_len(self): + desired = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ), + dict( + host="127.0.0.2", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ), + ], + ), + metadata=dict(name="openldap"), + ) + current = dict( + spec=dict( + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ), + metadata=dict( + name="openldap", + created_by="me", + ), + ) + assert ldap_auth_provider.do_differ(current, desired) is True + + def test_changes_are_other_params(self): + desired = dict( + spec=dict( + servers=[], + groups_prefix="ldap", + username_prefix="ldap", + ), + metadata=dict(name="openldap"), + ) + current = dict( + spec=dict( + servers=[], + ), + metadata=dict( + name="openldap", + created_by="me", + ), + ) + assert ldap_auth_provider.do_differ(current, desired) is True + + +class TestLDAPAutProvider(ModuleTestCase): + def test_minimal_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="openldap", + servers=[ + dict( + host="127.0.0.1", + group_search=dict( + base_dn="dc=acme,dc=org", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + ), + ) + ], + ) + + with pytest.raises(AnsibleExitJson): + ldap_auth_provider.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[ + 0 + ] + + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/openldap" + assert payload == dict( + type="ldap", + api_version="authentication/v2", + metadata=dict(name="openldap"), + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=None, + insecure=False, + security="tls", + trusted_ca_file=None, + client_cert_file=None, + client_key_file=None, + binding=None, + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="groupOfNames", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="uid", + name_attribute="cn", + object_class="person", + ), + ) + ] + ), + ) + + assert check_mode is False + + def test_all_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="openldap", + servers=[ + dict( + host="127.0.0.1", + port=636, + insecure=False, + security="tls", + trusted_ca_file="/path/to/trusted-certificate-authorities.pem", + client_cert_file="/path/to/ssl/cert.pem", + client_key_file="/path/to/ssl/key.pem", + binding=dict( + user_dn="cn=binder,dc=acme,dc=org", + password="YOUR_PASSWORD", + ), + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="groupOfNames", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="uid", + name_attribute="cn", + object_class="person", + ), + ) + ], + groups_prefix="ldap", + username_prefix="ldap", + ) + + with pytest.raises(AnsibleExitJson): + ldap_auth_provider.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[ + 0 + ] + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/openldap" + assert payload == dict( + type="ldap", + api_version="authentication/v2", + metadata=dict(name="openldap"), + spec=dict( + servers=[ + dict( + host="127.0.0.1", + port=636, + insecure=False, + security="tls", + trusted_ca_file="/path/to/trusted-certificate-authorities.pem", + client_cert_file="/path/to/ssl/cert.pem", + client_key_file="/path/to/ssl/key.pem", + binding=dict( + user_dn="cn=binder,dc=acme,dc=org", + password="YOUR_PASSWORD", + ), + group_search=dict( + base_dn="dc=acme,dc=org", + attribute="member", + name_attribute="cn", + object_class="groupOfNames", + ), + user_search=dict( + base_dn="dc=acme,dc=org", + attribute="uid", + name_attribute="cn", + object_class="person", + ), + ) + ], + groups_prefix="ldap", + username_prefix="ldap", + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + ldap_auth_provider.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py new file mode 100644 index 000000000..9cef32e76 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py @@ -0,0 +1,126 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import mutator + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + @pytest.mark.parametrize("current,desired", [ + ( # No diff in params, no secrets + dict(name="demo"), + dict(name="demo"), + ), + ( # No diff in params, no diff in secrets + dict(name="demo", secrets=[ + dict(name="n1", secret="s1"), dict(name="n2", secret="s2"), + ]), + dict(name="demo", secrets=[ + dict(name="n2", secret="s2"), dict(name="n1", secret="s1"), + ]), + ), + ]) + def test_no_difference(self, current, desired): + assert mutator.do_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired", [ + ( # Diff in params, no diff in secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="a", secret="1")]), + ), + ( # No diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="demo", secrets=[dict(name="b", secret="2")]), + ), + ( # Diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="b", secret="2")]), + ), + ]) + def test_difference(self, current, desired): + assert mutator.do_differ(current, desired) is True + + +class TestMutator(ModuleTestCase): + def test_minimal_mutator_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_mutator', + command='/bin/true', + ) + + with pytest.raises(AnsibleExitJson): + mutator.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/mutators/test_mutator' + assert payload == dict( + command='/bin/true', + metadata=dict( + name='test_mutator', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_mutator_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_mutator', + namespace='my', + state='absent', + command='/bin/true', + timeout=30, + runtime_assets='awesomeness', + labels={'region': 'us-west-1'}, + annotations={'playbook': 12345}, + secrets=[dict(name="a", secret="b")], + ) + + with pytest.raises(AnsibleExitJson): + mutator.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == 'absent' + assert path == '/api/core/v2/namespaces/my/mutators/test_mutator' + assert payload == dict( + command='/bin/true', + timeout=30, + runtime_assets=['awesomeness'], + metadata=dict( + name='test_mutator', + namespace='my', + labels={'region': 'us-west-1'}, + annotations={'playbook': '12345'}, + ), + secrets=[dict(name="a", secret="b")], + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_mutator', + command='/bion/true' + ) + + with pytest.raises(AnsibleFailJson): + mutator.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py new file mode 100644 index 000000000..b1456c8fb --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import mutator_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestMutatorInfo(ModuleTestCase): + def test_get_all_mutators(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + mutator_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/mutators" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_mutator(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-mutator") + + with pytest.raises(AnsibleExitJson) as context: + mutator_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/mutators/sample-mutator" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_mutator(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-mutator") + + with pytest.raises(AnsibleExitJson) as context: + mutator_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-mutator") + + with pytest.raises(AnsibleFailJson): + mutator_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py new file mode 100644 index 000000000..e51a63af1 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import namespace + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestNamespace(ModuleTestCase): + def test_namespace(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='dev' + ) + + with pytest.raises(AnsibleExitJson): + namespace.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/dev' + assert payload == dict( + name='dev' + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='dev', + ) + + with pytest.raises(AnsibleFailJson): + namespace.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py new file mode 100644 index 000000000..431b6463f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import namespace_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestNamespaceInfo(ModuleTestCase): + def test_get_namespaces(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + namespace_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + namespace_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py new file mode 100644 index 000000000..0780cdc2f --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, + utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import oidc_auth_provider + +from .common.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestADAutProvider(ModuleTestCase): + def test_minimal_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="oidc_name", + additional_scopes=["openid"], + client_id="a8e43af034e7f2608780", + client_secret="b63968394be6ed2edb61c93847ee792f31bf6216", + disable_offline_access=False, + server="https://oidc.example.com:9031", + username_claim="email", + ) + + with pytest.raises(AnsibleExitJson): + oidc_auth_provider.main() + + state, _client, path, payload, check_mode = sync_v1_mock.call_args[ + 0 + ] + + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name" + assert payload == dict( + type="oidc", + api_version="authentication/v2", + metadata=dict(name="oidc_name"), + spec=dict( + additional_scopes=["openid"], + client_id="a8e43af034e7f2608780", + client_secret="b63968394be6ed2edb61c93847ee792f31bf6216", + disable_offline_access=False, + server="https://oidc.example.com:9031", + username_claim="email", + ), + ) + + assert check_mode is False + + def test_all_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, "sync_v1") + sync_v1_mock.return_value = True, {} + set_module_args( + state="present", + name="oidc_name", + additional_scopes=["groups", "email", "username"], + client_id="a8e43af034e7f2608780", + client_secret="b63968394be6ed2edb61c93847ee792f31bf6216", + disable_offline_access=False, + redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback", + server="https://oidc.example.com:9031", + groups_claim="groups", + groups_prefix="oidc:", + username_claim="email", + username_prefix="oidc:", + ) + + with pytest.raises(AnsibleExitJson): + oidc_auth_provider.main() + + state, _client, path, payload, check_mode = sync_v1_mock.call_args[ + 0 + ] + assert state == "present" + assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name" + assert payload == dict( + type="oidc", + api_version="authentication/v2", + metadata=dict(name="oidc_name"), + spec=dict( + additional_scopes=["groups", "email", "username"], + client_id="a8e43af034e7f2608780", + client_secret="b63968394be6ed2edb61c93847ee792f31bf6216", + disable_offline_access=False, + redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback", + server="https://oidc.example.com:9031", + groups_claim="groups", + groups_prefix="oidc:", + username_claim="email", + username_prefix="oidc:", + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + oidc_auth_provider.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py new file mode 100644 index 000000000..69c3254a3 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py @@ -0,0 +1,136 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import pipe_handler + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + @pytest.mark.parametrize("current,desired", [ + ( # No diff in params, no secrets + dict(name="demo"), + dict(name="demo"), + ), + ( # No diff in params, no diff in secrets + dict(name="demo", secrets=[ + dict(name="n1", secret="s1"), dict(name="n2", secret="s2"), + ]), + dict(name="demo", secrets=[ + dict(name="n2", secret="s2"), dict(name="n1", secret="s1"), + ]), + ), + ]) + def test_no_difference(self, current, desired): + assert pipe_handler.do_differ(current, desired) is False + + @pytest.mark.parametrize("current,desired", [ + ( # Diff in params, no diff in secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="a", secret="1")]), + ), + ( # No diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="demo", secrets=[dict(name="b", secret="2")]), + ), + ( # Diff in params, missing and set secrets + dict(name="demo", secrets=[dict(name="a", secret="1")]), + dict(name="prod", secrets=[dict(name="b", secret="2")]), + ), + ]) + def test_difference(self, current, desired): + assert pipe_handler.do_differ(current, desired) is True + + +class TestPipeHandler(ModuleTestCase): + def test_minimal_pipe_handler_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_handler", + command='echo "test"' + ) + + with pytest.raises(AnsibleExitJson): + pipe_handler.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/core/v2/namespaces/default/handlers/test_handler" + assert payload == dict( + command='echo "test"', + type='pipe', + metadata=dict( + name="test_handler", + namespace="default", + ), + ) + assert check_mode is False + + def test_all_pipe_handler_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name='test_handler', + namespace='my', + state='absent', + command='/bin/true', + filters=['occurrences', 'production'], + mutator='only_check_output', + timeout=30, + env_vars=dict(foo='bar'), + runtime_assets='awesomeness', + secrets=[dict(name="a", secret="b")], + ) + + with pytest.raises(AnsibleExitJson): + pipe_handler.main() + + state, _client, path, payload, check_mode, _d = sync_mock.call_args[0] + assert state == "absent" + assert path == "/api/core/v2/namespaces/my/handlers/test_handler" + assert payload == dict( + command='/bin/true', + type='pipe', + filters=['occurrences', 'production'], + mutator='only_check_output', + timeout=30, + env_vars=['foo=bar'], + runtime_assets=['awesomeness'], + metadata=dict( + name="test_handler", + namespace="my", + ), + secrets=[dict(name="a", secret="b")], + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_handler', + state='absent', + command='/bin/true', + filters=['occurrences', 'production'], + mutator='only_check_output', + timeout=30, + env_vars=dict(foo='bar'), + runtime_assets='awesomeness' + ) + + with pytest.raises(AnsibleFailJson): + pipe_handler.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py new file mode 100644 index 000000000..2339e8551 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import handler_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestHandlerInfo(ModuleTestCase): + def test_get_all_handlers(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + handler_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/handlers" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_handler(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-handler") + + with pytest.raises(AnsibleExitJson) as context: + handler_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/handlers/sample-handler" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_handler(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-handler") + + with pytest.raises(AnsibleExitJson) as context: + handler_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-handler") + + with pytest.raises(AnsibleFailJson): + handler_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py new file mode 100644 index 000000000..cd1ce4fbf --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py @@ -0,0 +1,142 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import role + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestRole(ModuleTestCase): + def test_minimal_role_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role', + rules=[ + dict( + verbs=[], + resources=[] + ), + ] + ) + + with pytest.raises(AnsibleExitJson): + role.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/roles/test_role' + assert payload == dict( + rules=[ + dict( + verbs=[], + resources=[], + resource_names=None, + ) + ], + metadata=dict( + name='test_role', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_role_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role', + namespace='my', + state='present', + rules=[ + dict( + verbs=['get', 'list', 'create'], + resources=['assets', 'entities'], + ), + dict( + verbs=['list'], + resources=['check'], + resource_names=['my-check'], + ) + ], + ) + + with pytest.raises(AnsibleExitJson): + role.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/my/roles/test_role' + assert payload == dict( + metadata=dict( + name='test_role', + namespace='my', + ), + rules=[ + dict( + verbs=['get', 'list', 'create'], + resources=['assets', 'entities'], + resource_names=None, + ), + dict( + verbs=['list'], + resources=['check'], + resource_names=['my-check'], + ) + ], + ) + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_role', + rules=[ + dict( + verbs=[], + resources=[], + ) + ], + ) + with pytest.raises(AnsibleFailJson): + role.main() + + def test_failure_invalid_verb(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_role', + rules=[ + dict( + verbs=['list', 'invalid'], + resources=[], + ), + ] + ) + + with pytest.raises(AnsibleFailJson): + role.main() + + def test_failure_empty_rules(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_role', + rules=[] + ) + + with pytest.raises(AnsibleFailJson): + role.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py new file mode 100644 index 000000000..b22eaa7ef --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py @@ -0,0 +1,214 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import role_binding + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestRoleBinding(ModuleTestCase): + def test_minimal_role_binding_parameters_users(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role_binding', + role='test_role', + users=['test_user'], + ) + + with pytest.raises(AnsibleExitJson): + role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding' + assert payload == dict( + role_ref=dict( + name='test_role', + type='Role', + ), + subjects=[ + dict( + name='test_user', + type='User', + ), + ], + metadata=dict( + name='test_role_binding', + namespace='default', + ), + ) + assert check_mode is False + + def test_minimal_role_binding_parameters_groups(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role_binding', + role='test_role', + groups=['test_group'], + ) + + with pytest.raises(AnsibleExitJson): + role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding' + assert payload == dict( + role_ref=dict( + name='test_role', + type='Role', + ), + subjects=[ + dict( + name='test_group', + type='Group', + ), + ], + metadata=dict( + name='test_role_binding', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_role_binding_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role_binding', + namespace='my', + role='test_role', + users=['user_1', 'user_2'], + groups=['group_1', 'group_2'], + ) + + with pytest.raises(AnsibleExitJson): + role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/my/rolebindings/test_role_binding' + assert payload == dict( + metadata=dict( + name='test_role_binding', + namespace='my', + ), + role_ref=dict( + name='test_role', + type='Role', + ), + subjects=[ + dict( + name='group_1', + type='Group', + ), + dict( + name='group_2', + type='Group', + ), + dict( + name='user_1', + type='User', + ), + dict( + name='user_2', + type='User', + ), + ] + ) + assert check_mode is False + + def test_role_binding_with_cluster_role(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_role_binding', + cluster_role='test_cluster_role', + users=['test_user'], + ) + + with pytest.raises(AnsibleExitJson): + role_binding.main() + + state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding' + assert payload == dict( + role_ref=dict( + name='test_cluster_role', + type='ClusterRole', + ), + subjects=[ + dict( + name='test_user', + type='User', + ), + ], + metadata=dict( + name='test_role_binding', + namespace='default', + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_role_binding', + role='test_role', + users=['test_user'], + ) + with pytest.raises(AnsibleFailJson): + role_binding.main() + + def test_failure_role_and_cluster_role(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_role_binding', + role='test_role', + cluster_role='test_cluster_role', + ) + + with pytest.raises(AnsibleFailJson): + role_binding.main() + + def test_failure_missing_groups_or_users(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = Exception("Validation should fail but didn't") + set_module_args( + name='test_role_binding', + role='test_role', + ) + + with pytest.raises(AnsibleFailJson): + role_binding.main() + + @pytest.mark.parametrize("params,result", [ + ( + dict(role="test-role", cluster_role=None), + ("Role", "test-role"), + ), + ( + dict(cluster_role="test-cluster-role", role=None), + ("ClusterRole", "test-cluster-role"), + ), + ]) + def test_infer_role(self, params, result): + assert result == role_binding.infer_role(params) diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py new file mode 100644 index 000000000..2f7725160 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import role_binding_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestRoleBindingInfo(ModuleTestCase): + def test_get_all_role_bindings(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + role_binding_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/rolebindings" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_role_binding(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 1 + set_module_args(name="test-role-binding") + + with pytest.raises(AnsibleExitJson) as context: + role_binding_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/rolebindings/test-role-binding" + assert context.value.args[0]["objects"] == [1] + + def test_missing_single_role_binding(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-role-binding") + + with pytest.raises(AnsibleExitJson) as context: + role_binding_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-role-binding") + + with pytest.raises(AnsibleFailJson): + role_binding_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py new file mode 100644 index 000000000..a2bec291d --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import role_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestRoleInfo(ModuleTestCase): + def test_get_all_roles(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + role_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/roles" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_role(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 1 + set_module_args(name="test-role") + + with pytest.raises(AnsibleExitJson) as context: + role_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/roles/test-role" + assert context.value.args[0]["objects"] == [1] + + def test_missing_single_role(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-role") + + with pytest.raises(AnsibleExitJson) as context: + role_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-role") + + with pytest.raises(AnsibleFailJson): + role_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py new file mode 100644 index 000000000..5912ae738 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import secret + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSecret(ModuleTestCase): + @pytest.mark.parametrize("params", [ + {"name": "demo", "provider": "env", "id": "MY_VAR"}, + {"name": "demo", "state": "absent"}, + ]) + def test_minimal_parameters(self, mocker, params): + mocker.patch.object(utils, "sync_v1").return_value = True, {} + set_module_args(**params) + + with pytest.raises(AnsibleExitJson): + secret.main() + + def test_all_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync_v1") + sync_mock.return_value = True, {} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + state="present", + name="demo", + namespace="ns", + provider="env", + id="MY_ENV_VAR", + ) + + with pytest.raises(AnsibleExitJson): + secret.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/enterprise/secrets/v1/namespaces/ns/secrets/demo" + assert payload == dict( + type="Secret", + api_version="secrets/v1", + metadata=dict(name="demo", namespace="ns"), + spec=dict(provider="env", id="MY_ENV_VAR"), + ) + assert check_mode is False + + @pytest.mark.parametrize("skip", ["provider", "id"]) + def test_missing_required_param_present(self, mocker, skip): + sync_mock = mocker.patch.object(utils, "sync_v1") + all_args = dict(name="demo", provider="env", id="X") + set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip)) + + with pytest.raises(AnsibleFailJson): + secret.main() + + sync_mock.assert_not_called() + + def test_failure(self, mocker): + mocker.patch.object(utils, "sync_v1").side_effect = ( + errors.Error("Bad error") + ) + set_module_args(name="demo", state="absent") + + with pytest.raises(AnsibleFailJson): + secret.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py new file mode 100644 index 000000000..161204f26 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import secret_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSecretInfo(ModuleTestCase): + def test_all_parameters(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k1": "v1"}} + set_module_args( + auth=dict( + user="user", + password="pass", + url="http://127.0.0.1:1234", + api_key="123-key", + verify=False, + ca_path="/tmp/ca.bundle", + ), + name="demo", + namespace="ns", + ) + + with pytest.raises(AnsibleExitJson): + secret_info.main() + + def test_get_all_secrets(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [ + {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}}, + ] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + secret_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/secrets/v1/namespaces/default/secrets" + assert context.value.args[0]["objects"] == [ + {"k1": "v1"}, {"k2": "v2"}, + ] + + def test_get_single_secret(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = {"spec": {"k3": "v3"}} + set_module_args(name="demo", namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + secret_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/secrets/v1/namespaces/my/secrets/demo" + assert context.value.args[0]["objects"] == [{"k3": "v3"}] + + def test_missing_single_secret(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="demo") + + with pytest.raises(AnsibleExitJson) as context: + secret_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="demo") + + with pytest.raises(AnsibleFailJson): + secret_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py new file mode 100644 index 000000000..cce081bf9 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_env + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSecretsProviderEnv(ModuleTestCase): + def test_no_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, 'sync_v1') + sync_v1_mock.return_value = True, {} + set_module_args() + + with pytest.raises(AnsibleExitJson): + secrets_provider_env.main() + + state, _client, path, payload, check_mode = sync_v1_mock.call_args[0] + assert state == 'present' + assert path == '/api/enterprise/secrets/v1/providers/env' + assert payload == dict( + type='Env', + api_version="secrets/v1", + metadata=dict(name='env'), + spec={} + ) + assert check_mode is False + + def test_all_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, 'sync_v1') + sync_v1_mock.return_value = True, {} + set_module_args( + state='present', + ) + + with pytest.raises(AnsibleExitJson): + secrets_provider_env.main() + + state, _client, path, payload, check_mode = sync_v1_mock.call_args[0] + assert state == 'present' + assert path == '/api/enterprise/secrets/v1/providers/env' + assert payload == dict( + type='Env', + api_version="secrets/v1", + metadata=dict(name='env'), + spec={} + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_v1_mock = mocker.patch.object(utils, 'sync_v1') + sync_v1_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + secrets_provider_env.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py new file mode 100644 index 000000000..695152493 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSecretsProviderInfo(ModuleTestCase): + def test_get_all_secrets_providers(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [dict(spec=1), dict(spec=2)] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + secrets_provider_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/secrets/v1/providers" + assert context.value.args[0]["objects"] == [1, 2] + + def test_get_single_secrets_provider(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = dict(spec=4) + set_module_args(name="sample-secrets-provider") + + with pytest.raises(AnsibleExitJson) as context: + secrets_provider_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/enterprise/secrets/v1/providers/sample-secrets-provider" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_secrets_provider(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-secrets-provider") + + with pytest.raises(AnsibleExitJson) as context: + secrets_provider_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-secrets-provider") + + with pytest.raises(AnsibleFailJson): + secrets_provider_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py new file mode 100644 index 000000000..fc4c17a87 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py @@ -0,0 +1,182 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_vault + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestDoDiffer: + def test_fields_are_ignored(self): + desired = dict( + spec=dict( + client=dict( + address="https://my-vault.com", + tls=dict( + ca_cert="cert" + ), + ), + ), + metadata=dict( + name="my-vault" + ) + ) + current = dict( + spec=dict( + client=dict( + address="https://my-vault.com", + agent_address="", # extra field + tls=dict( + ca_cert="cert", + # extra fields + insecure=False, + ca_path="path", + tls_server_name="server", + ) + ), + ), + metadata=dict( + name="my-vault", + created_by="me", + ) + ) + + assert secrets_provider_vault.do_differ(current, desired) is False + + def test_changes_are_detected(self): + desired = dict( + spec=dict( + client=dict( + address="https://my-vault.com", + tls=dict( + ca_cert="cert" + ), + ), + ), + metadata=dict( + name="my-vault" + ) + ) + current = dict( + spec=dict( + client=dict( + address="https://my-vault.com", + tls=dict( + ca_cert="new-cert", + cname="server", + ), + timeout='15s', + ), + ), + metadata=dict( + name="my-vault", + ) + ) + assert secrets_provider_vault.do_differ(current, desired) is True + + +class TestSecretsProviderVault(ModuleTestCase): + def test_minimal_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, 'sync_v1') + sync_v1_mock.return_value = True, {} + set_module_args( + name='my-vault', + state='present', + address='https://my-vault.com', + token='AUTH_TOKEN', + version='v1', + ) + + with pytest.raises(AnsibleExitJson): + secrets_provider_vault.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0] + assert state == 'present' + assert path == '/api/enterprise/secrets/v1/providers/my-vault' + assert payload == dict( + type='VaultProvider', + api_version="secrets/v1", + metadata=dict(name='my-vault'), + spec=dict( + client=dict( + address='https://my-vault.com', + token='AUTH_TOKEN', + version='v1', + ), + ), + ) + assert check_mode is False + + def test_all_provider_parameters(self, mocker): + sync_v1_mock = mocker.patch.object(utils, 'sync_v1') + sync_v1_mock.return_value = True, {} + set_module_args( + name='my-vault', + state='present', + address='https://my-vault.com', + token='AUTH_TOKEN', + version='v1', + tls=dict( + ca_cert='/etc/ssl/ca.crt', + client_cert='/etc/ssl/client.crt', + client_key='/etc/ssl/client.key', + cname='my-vault.com', + ), + timeout=1, + max_retries=2, + rate_limit=3, + burst_limit=4, + ) + + with pytest.raises(AnsibleExitJson): + secrets_provider_vault.main() + + state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0] + assert state == 'present' + assert path == '/api/enterprise/secrets/v1/providers/my-vault' + assert payload == dict( + type='VaultProvider', + api_version="secrets/v1", + metadata=dict(name='my-vault'), + spec=dict( + client=dict( + address='https://my-vault.com', + token='AUTH_TOKEN', + version='v1', + tls=dict( + ca_cert='/etc/ssl/ca.crt', + client_cert='/etc/ssl/client.crt', + client_key='/etc/ssl/client.key', + cname='my-vault.com', + ), + timeout="1s", + max_retries=2, + rate_limiter=dict( + limit=3, + burst=4, + ), + ), + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync_v1') + sync_mock.side_effect = errors.Error("Bad error") + set_module_args() + + with pytest.raises(AnsibleFailJson): + secrets_provider_vault.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py new file mode 100644 index 000000000..ad0642718 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py @@ -0,0 +1,120 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import silence + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSilence(ModuleTestCase): + def test_minimal_silence_parameters_check(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + check='check' + ) + + with pytest.raises(AnsibleExitJson): + silence.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/silenced/%2A%3Acheck' # %2A = *, %3A = : + assert payload == dict( + check='check', + metadata=dict( + name='*:check', + namespace='default', + ), + ) + assert check_mode is False + + def test_minimal_silence_parameters_subscription(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + subscription='subscription' + ) + + with pytest.raises(AnsibleExitJson): + silence.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'present' + assert path == '/api/core/v2/namespaces/default/silenced/subscription%3A%2A' # %3A = :, %2A = * + assert payload == dict( + subscription='subscription', + metadata=dict( + name='subscription:*', + namespace='default', + ), + ) + assert check_mode is False + + def test_all_silence_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.return_value = True, {} + set_module_args( + namespace='my', + subscription='entity:test-entity', + check='check', + state='absent', + begin=1542671205, + expire=1542771205, + expire_on_resolve=True, + reason='because', + labels={'region': 'us-west-1'}, + annotations={'playbook': 12345}, + ) + + with pytest.raises(AnsibleExitJson): + silence.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == 'absent' + assert path == '/api/core/v2/namespaces/my/silenced/entity%3Atest-entity%3Acheck' # %3A = : + assert payload == dict( + subscription='entity:test-entity', + check='check', + begin=1542671205, + expire=1542771205, + expire_on_resolve=True, + reason='because', + metadata=dict( + name='entity:test-entity:check', + namespace='my', + labels={'region': 'us-west-1'}, + annotations={'playbook': '12345'}, + ), + ) + assert check_mode is False + + def test_failure_when_both_params_are_missing(self): + set_module_args() + + with pytest.raises(AnsibleFailJson, + match='one of the following is required: subscription, check'): + silence.main() + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + subscription='subscription' + ) + + with pytest.raises(AnsibleFailJson): + silence.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py new file mode 100644 index 000000000..7a2215411 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py @@ -0,0 +1,66 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import silence_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSilenceInfo(ModuleTestCase): + def test_get_all_silences(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args(namespace="my") + + with pytest.raises(AnsibleExitJson) as context: + silence_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/my/silenced" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_silence(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(subscription="subscription") + + with pytest.raises(AnsibleExitJson) as context: + silence_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/namespaces/default/silenced/subscription%3A%2A" # %3A = :, %2A = * + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_silence(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args( + subscription="missing", + check="missing", + ) + + with pytest.raises(AnsibleExitJson) as context: + silence_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(check="check") + + with pytest.raises(AnsibleFailJson): + silence_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py new file mode 100644 index 000000000..b4c72f194 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py @@ -0,0 +1,101 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import socket_handler + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSocketHandler(ModuleTestCase): + def test_minimal_socket_handler_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name="test_handler", + type='tcp', + host='10.0.1.99', + port=4444 + ) + + with pytest.raises(AnsibleExitJson): + socket_handler.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "present" + assert path == "/api/core/v2/namespaces/default/handlers/test_handler" + assert payload == dict( + type='tcp', + socket=dict( + host='10.0.1.99', + port=4444 + ), + metadata=dict( + name="test_handler", + namespace="default", + ), + ) + assert check_mode is False + + def test_all_socket_handler_parameters(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.return_value = True, {} + set_module_args( + name='test_handler', + namespace='my', + state='absent', + type='udp', + filters=['occurrences', 'production'], + mutator='only_check_output', + timeout=30, + host='10.0.1.99', + port=4444 + ) + + with pytest.raises(AnsibleExitJson): + socket_handler.main() + + state, _client, path, payload, check_mode = sync_mock.call_args[0] + assert state == "absent" + assert path == "/api/core/v2/namespaces/my/handlers/test_handler" + assert payload == dict( + type='udp', + filters=['occurrences', 'production'], + mutator='only_check_output', + timeout=30, + socket=dict( + host='10.0.1.99', + port=4444 + ), + metadata=dict( + name="test_handler", + namespace="my", + ), + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(utils, "sync") + sync_mock.side_effect = errors.Error("Bad error") + set_module_args( + name='test_handler', + state='absent', + type='udp', + host='10.0.1.99', + port=4444 + ) + + with pytest.raises(AnsibleFailJson): + socket_handler.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py new file mode 100644 index 000000000..3eb2796b9 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py @@ -0,0 +1,105 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, http, +) +from ansible_collections.sensu.sensu_go.plugins.modules import tessen + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestSync: + def test_remote_and_desired_equal(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + changed, object = tessen.sync(client, "/path", {}, False) + + assert changed is False + assert object == {} + + def test_remote_and_desired_not_equal(self, mocker): + client = mocker.Mock() + client.get.side_effect = ( + http.Response(200, '{"opt_out": "false"}'), + http.Response(200, '{"opt_out": "true"}'), + ) + client.put.return_value = http.Response(200, "") + changed, object = tessen.sync(client, "/path", {'opt_out': True}, False) + + assert changed is True + assert object == {'opt_out': 'true'} + client.put.assert_called_once_with("/path", {'opt_out': True}) + + def test_remote_and_desired_equal_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{}') + changed, object = tessen.sync(client, "/path", {}, True) + + assert changed is False + assert object == {} + + def test_remote_and_desired_not_equal_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"opt_out": "false"}') + changed, object = tessen.sync(client, "/path", {'opt_out': True}, True) + + assert changed is True + assert object == {'opt_out': True} + client.put.assert_not_called() + + +class TestTessen(ModuleTestCase): + def test_enabled(self, mocker): + sync_mock = mocker.patch.object(tessen, 'sync') + sync_mock.return_value = True, {} + set_module_args( + state='enabled' + ) + + with pytest.raises(AnsibleExitJson): + tessen.main() + + _client, path, payload, check_mode = sync_mock.call_args[0] + assert path == '/api/core/v2/tessen' + assert payload == dict( + opt_out=False + ) + assert check_mode is False + + def test_disabled(self, mocker): + sync_mock = mocker.patch.object(tessen, 'sync') + sync_mock.return_value = True, {} + set_module_args( + state='disabled' + ) + + with pytest.raises(AnsibleExitJson): + tessen.main() + + _client, path, payload, check_mode = sync_mock.call_args[0] + assert path == '/api/core/v2/tessen' + assert payload == dict( + opt_out=True + ) + assert check_mode is False + + def test_failure(self, mocker): + sync_mock = mocker.patch.object(tessen, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + state='enabled' + ) + + with pytest.raises(AnsibleFailJson): + tessen.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py new file mode 100644 index 000000000..52e4a7698 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py @@ -0,0 +1,519 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +from distutils import version + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + arguments, errors, http, utils +) +from ansible_collections.sensu.sensu_go.plugins.modules import user + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestUpdatePassword: + @pytest.mark.parametrize('check', [False, True]) + def test_password_is_valid(self, mocker, check): + client = mocker.Mock() + client.validate_auth_data.return_value = True + + changed = user.update_password(client, '/path', 'user', 'pass', check) + + assert changed is False + client.validate_auth_data.assert_called_once_with('user', 'pass') + client.put.assert_not_called() + + def test_password_is_invalid_older_than_5_21_0(self, mocker): + client = mocker.Mock() + client.validate_auth_data.return_value = False + client.version = version.StrictVersion("5.20.2") + client.put.return_value = http.Response(201, '') + + changed = user.update_password(client, '/path', 'user', 'pass', False) + + assert changed is True + client.validate_auth_data.assert_called_once_with('user', 'pass') + client.put.assert_called_once_with('/path/password', dict( + username='user', password='pass', + )) + + def test_password_is_invalid_5_21_0_or_newer(self, mocker): + client = mocker.Mock() + client.validate_auth_data.return_value = False + client.version = version.StrictVersion("5.21.0") + client.put.return_value = http.Response(201, '') + + changed = user.update_password(client, '/path', 'user', 'pass', False) + + assert changed is True + client.validate_auth_data.assert_called_once_with('user', 'pass') + client.put.assert_called_once() + + path, payload = client.put.call_args[0] + assert path == '/path/reset_password' + assert payload['username'] == 'user' + + # (tadeboro): We cannot validate the value without mocking the bcrypt. + # And I would rather see that our code gets tested by actually using + # the bcrypt rather than mocking it out. This way, the message + # encode/decode stuff gets put through its paces. + assert 'password_hash' in payload + + def test_password_is_invalid_check_mode(self, mocker): + client = mocker.Mock() + client.validate_auth_data.return_value = False + + changed = user.update_password(client, '/path', 'user', 'pass', True) + + assert changed is True + client.validate_auth_data.assert_called_once_with('user', 'pass') + client.put.assert_not_called() + + +class TestUpdatePasswordHash: + @pytest.mark.parametrize('check', [False, True]) + def test_sensu_go_older_than_5_21_0(self, mocker, check): + client = mocker.Mock() + client.version = version.StrictVersion("5.20.0") + + with pytest.raises(errors.SensuError): + user.update_password_hash(client, '/path', 'user', 'hash', check) + + client.put.assert_not_called() + + def test_sensu_go_newer_than_5_21_0(self, mocker): + client = mocker.Mock() + client.version = version.StrictVersion("5.21.0") + client.put.return_value = http.Response(201, '') + + changed = user.update_password_hash( + client, '/path', 'user', 'hash', False, + ) + + assert changed is True + client.put.assert_called_once() + + path, payload = client.put.call_args[0] + assert path == '/path/reset_password' + assert payload['username'] == 'user' + assert payload['password_hash'] == 'hash' + + def test_sensu_go_newer_than_5_21_0_check_mode(self, mocker): + client = mocker.Mock() + client.version = version.StrictVersion("5.21.0") + + changed = user.update_password_hash( + client, '/path', 'user', 'pass', True, + ) + + assert changed is True + client.put.assert_not_called() + + +class TestUpdateGroups: + @pytest.mark.parametrize('check', [False, True]) + def test_update_groups_no_change(self, mocker, check): + client = mocker.Mock() + + result = user.update_groups( + client, '/path', ['a', 'b'], ['b', 'a'], check, + ) + + assert result is False + client.put.assert_not_called() + client.delete.assert_not_called() + + def test_update_groups(self, mocker): + client = mocker.Mock() + client.put.side_effect = [ + http.Response(201, ''), http.Response(201, ''), + ] + client.delete.side_effect = [ + http.Response(204, ''), http.Response(204, ''), + ] + + result = user.update_groups( + client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], False, + ) + + assert result is True + client.put.assert_has_calls([ + mocker.call('/path/groups/d', None), + mocker.call('/path/groups/e', None), + ], any_order=True) + client.delete.assert_has_calls([ + mocker.call('/path/groups/a'), + mocker.call('/path/groups/b'), + ], any_order=True) + + def test_update_groups_check_mode(self, mocker): + client = mocker.Mock() + + result = user.update_groups( + client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], True, + ) + + assert result is True + client.put.assert_not_called() + client.delete.assert_not_called() + + +class TestUpdateState: + @pytest.mark.parametrize('check', [False, True]) + @pytest.mark.parametrize('state', [False, True]) + def test_update_state_no_change(self, mocker, check, state): + client = mocker.Mock() + + result = user.update_state(client, '/path', state, state, check) + + assert result is False + client.put.assert_not_called() + client.delete.assert_not_called() + + def test_disable_user(self, mocker): + client = mocker.Mock() + client.delete.return_value = http.Response(204, '') + + # Go from disabled == False to disabled == True + result = user.update_state(client, '/path', False, True, False) + + assert result is True + client.put.assert_not_called() + client.delete.assert_called_once_with('/path') + + def test_disable_user_check_mode(self, mocker): + client = mocker.Mock() + + # Go from disabled == False to disabled == True + result = user.update_state(client, '/path', False, True, True) + + assert result is True + client.put.assert_not_called() + client.delete.assert_not_called() + + def test_enable_user(self, mocker): + client = mocker.Mock() + client.put.return_value = http.Response(201, '') + + # Go from disabled == True to disabled == False + result = user.update_state(client, '/path', True, False, False) + + assert result is True + client.put.assert_called_once_with('/path/reinstate', None) + client.delete.assert_not_called() + + def test_enable_user_check_mode(self, mocker): + client = mocker.Mock() + + # Go from disabled == True to disabled == False + result = user.update_state(client, '/path', True, False, True) + + assert result is True + client.put.assert_not_called() + client.delete.assert_not_called() + + +class TestSync: + def test_no_current_object(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + client.put.return_value = http.Response(201, '') + + changed, result = user.sync( + None, client, '/path', {'password': 'data'}, False + ) + + assert changed is True + assert {'new': 'data'} == result + client.put.assert_called_once_with('/path', {'password': 'data'}) + + def test_no_current_object_check(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + + changed, result = user.sync( + None, client, '/path', {'password_hash': 'data'}, True + ) + + assert changed is True + assert {} == result + client.put.assert_not_called() + + def test_password_update(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + p_mock.return_value = True + g_mock = mocker.patch.object(user, 'update_groups') + s_mock = mocker.patch.object(user, 'update_state') + + changed, result = user.sync( + dict(old='data'), client, '/path', + dict(username='user', password='pass'), False + ) + + assert changed is True + assert dict(new='data') == result + p_mock.assert_called_once() + g_mock.assert_not_called() + s_mock.assert_not_called() + + def test_password_update_check_mode(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + p_mock.return_value = False + g_mock = mocker.patch.object(user, 'update_groups') + s_mock = mocker.patch.object(user, 'update_state') + + changed, result = user.sync( + dict(old='data'), client, '/path', + dict(username='user', password='pass'), True + ) + + assert changed is False + assert dict(old='data', username='user') == result + p_mock.assert_called_once() + g_mock.assert_not_called() + s_mock.assert_not_called() + + def test_password_hash_update(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + mock = mocker.patch.object(user, 'update_password_hash') + mock.return_value = True + + changed, result = user.sync( + dict(old='data'), client, '/path', + dict(username='user', password_hash='pass'), False + ) + + assert changed is True + assert dict(new='data') == result + mock.assert_called_once() + + def test_password_hash_update_check_mode(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + mock = mocker.patch.object(user, 'update_password_hash') + mock.return_value = True + + changed, result = user.sync( + dict(old='data'), client, '/path', + dict(username='user', password_hash='pass'), True + ) + + assert changed is True + assert dict(old='data', username='user') == result + mock.assert_called_once() + + def test_when_password_is_set_we_ignore_hash(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + p_mock.return_value = True + h_mock = mocker.patch.object(user, 'update_password_hash') + + user.sync( + dict(old='data'), client, '/path', + dict(username='user', password='pass', password_hash='hash'), + False + ) + + p_mock.assert_called_once() + h_mock.assert_not_called() + + def test_groups_update(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + g_mock = mocker.patch.object(user, 'update_groups') + g_mock.return_value = False + s_mock = mocker.patch.object(user, 'update_state') + + changed, result = user.sync( + dict(groups=['a']), client, '/path', dict(groups=['b']), False + ) + + assert changed is False + assert dict(new='data') == result + p_mock.assert_not_called() + g_mock.assert_called_once() + s_mock.assert_not_called() + + def test_groups_update_check_mode(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + g_mock = mocker.patch.object(user, 'update_groups') + g_mock.return_value = True + s_mock = mocker.patch.object(user, 'update_state') + + changed, result = user.sync( + dict(x=3, groups=['a']), client, '/path', dict(groups=['b']), True + ) + + assert changed is True + assert dict(x=3, groups=['b']) == result + p_mock.assert_not_called() + g_mock.assert_called_once() + s_mock.assert_not_called() + + def test_state_update(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + g_mock = mocker.patch.object(user, 'update_groups') + s_mock = mocker.patch.object(user, 'update_state') + s_mock.return_value = False + + changed, result = user.sync( + dict(disabled=True), client, '/path', dict(disabled=False), False + ) + + assert changed is False + assert dict(new='data') == result + p_mock.assert_not_called() + g_mock.assert_not_called() + s_mock.assert_called_once() + + def test_state_update_check_mode(self, mocker): + client = mocker.Mock() + client.get.return_value = http.Response(200, '{"new": "data"}') + p_mock = mocker.patch.object(user, 'update_password') + g_mock = mocker.patch.object(user, 'update_groups') + s_mock = mocker.patch.object(user, 'update_state') + s_mock.return_value = True + + changed, result = user.sync( + dict(disabled=True), client, '/path', dict(disabled=False), True + ) + + assert changed is True + assert dict(disabled=False) == result + p_mock.assert_not_called() + g_mock.assert_not_called() + s_mock.assert_called_once() + + +class TestUser(ModuleTestCase): + def test_minimal_user_parameters(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = None + sync_mock = mocker.patch.object(user, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='alice', + password='alice!?pass', + ) + + with pytest.raises(AnsibleExitJson): + user.main() + + result, _client, path, payload, check_mode = sync_mock.call_args[0] + assert path == '/api/core/v2/users/alice' + assert payload == dict( + username='alice', + password='alice!?pass', + disabled=False + ) + assert check_mode is False + + def test_minimal_parameters_on_existing_user(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = dict(username='alice') + sync_mock = mocker.patch.object(user, 'sync') + sync_mock.return_value = True, {} + set_module_args(name='alice') + + with pytest.raises(AnsibleExitJson): + user.main() + + result, _client, path, payload, check_mode = sync_mock.call_args[0] + assert path == '/api/core/v2/users/alice' + assert payload == dict(username='alice', disabled=False) + assert check_mode is False + + def test_all_user_parameters(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = None + sync_mock = mocker.patch.object(user, 'sync') + sync_mock.return_value = True, {} + set_module_args( + name='test_user', + state='disabled', + password='password', + groups=['dev', 'ops'], + ) + + with pytest.raises(AnsibleExitJson): + user.main() + + result, _client, path, payload, check_mode = sync_mock.call_args[0] + assert path == '/api/core/v2/users/test_user' + assert payload == dict( + username='test_user', + password='password', + groups=['dev', 'ops'], + disabled=True + ) + assert check_mode is False + + def test_cannot_create_user_without_password(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = None + set_module_args( + name='test_user', + state='disabled', + groups=['dev', 'ops'], + ) + + with pytest.raises(AnsibleFailJson, match='without a password'): + user.main() + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.return_value = None + sync_mock = mocker.patch.object(user, 'sync') + sync_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_user', + password='password' + ) + + with pytest.raises(AnsibleFailJson): + user.main() + + def test_failure_on_initial_get(self, mocker): + get_mock = mocker.patch.object(utils, 'get') + get_mock.side_effect = errors.Error('Bad error') + set_module_args( + name='test_user', + password='password' + ) + + with pytest.raises(AnsibleFailJson): + user.main() + + def test_failure_on_missing_bcrypt_5_21_0_or_newer(self, mocker): + mocker.patch.object(arguments, 'get_sensu_client').return_value = ( + mocker.MagicMock(version='5.22.3') + ) + mocker.patch.object(user, 'HAS_BCRYPT', False) + set_module_args( + name='test_user', + password='password' + ) + + with pytest.raises(AnsibleFailJson, match='bcrypt'): + user.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py new file mode 100644 index 000000000..09fc337f0 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import sys + +import pytest + +from ansible_collections.sensu.sensu_go.plugins.module_utils import ( + errors, utils, +) +from ansible_collections.sensu.sensu_go.plugins.modules import user_info + +from .common.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, +) + +pytestmark = pytest.mark.skipif( + sys.version_info < (2, 7), reason="requires python2.7 or higher" +) + + +class TestUserInfo(ModuleTestCase): + def test_get_all_users(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = [1, 2, 3] + set_module_args() + + with pytest.raises(AnsibleExitJson) as context: + user_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/users" + assert context.value.args[0]["objects"] == [1, 2, 3] + + def test_get_single_user(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = 4 + set_module_args(name="sample-user") + + with pytest.raises(AnsibleExitJson) as context: + user_info.main() + + _client, path = get_mock.call_args[0] + assert path == "/api/core/v2/users/sample-user" + assert context.value.args[0]["objects"] == [4] + + def test_missing_single_user(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.return_value = None + set_module_args(name="sample-user") + + with pytest.raises(AnsibleExitJson) as context: + user_info.main() + + assert context.value.args[0]["objects"] == [] + + def test_failure(self, mocker): + get_mock = mocker.patch.object(utils, "get") + get_mock.side_effect = errors.Error("Bad error") + set_module_args(name="sample-user") + + with pytest.raises(AnsibleFailJson): + user_info.main() diff --git a/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt new file mode 100644 index 000000000..7f0b6e759 --- /dev/null +++ b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt @@ -0,0 +1 @@ +bcrypt |