diff --git a/constraints.txt b/constraints.txt index 0c87fd7f..359bb881 100644 --- a/constraints.txt +++ b/constraints.txt @@ -15,10 +15,12 @@ jsonschema==3.2.0 # CSM 1.6 uses Kubernetes v1.24 kubernetes==24.2.0 oauthlib==3.2.2 +packaging==21.3.0 pyasn1==0.5.1 pyasn1-modules==0.3.0 pyrsistent==0.18.0 python-dateutil==2.9.0 +python-keycloak==0.27.1 PyYAML==6.0.1 requests==2.27.1 requests-oauthlib==2.0.0 diff --git a/goss-testing/scripts/check_iuf_abort.sh b/goss-testing/scripts/check_iuf_abort.sh new file mode 100644 index 00000000..6cbd99b7 --- /dev/null +++ b/goss-testing/scripts/check_iuf_abort.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +/opt/cray/tests/install/ncn/scripts/python/iuf_run /opt/cray/tests/install/ncn/scripts/iuf_run_setup test-activity && \ +sleep 10 && \ +iuf -a test-activity abort -f diff --git a/goss-testing/scripts/iuf_run_setup/dummy-1.0.0.tar.gz b/goss-testing/scripts/iuf_run_setup/dummy-1.0.0.tar.gz new file mode 100644 index 00000000..b365bb26 Binary files /dev/null and b/goss-testing/scripts/iuf_run_setup/dummy-1.0.0.tar.gz differ diff --git a/goss-testing/scripts/iuf_run_setup/management-bootprep.yaml b/goss-testing/scripts/iuf_run_setup/management-bootprep.yaml new file mode 100644 index 00000000..1e93b680 --- /dev/null +++ b/goss-testing/scripts/iuf_run_setup/management-bootprep.yaml @@ -0,0 +1,54 @@ +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +--- +schema_version: 1.0.2 +configurations: +- name: "config-minimal-management-dummy-1.0.0" + layers: + - name: csm-ncn-nodes-{{csm.version}} + playbook: ncn_nodes.yml + product: + name: csm + version: "{{csm.version}}" + - name: csm-ncn-initrd-{{csm.version}} + playbook: ncn-initrd.yml + product: + name: csm + version: "{{csm.version}}" + +images: +- name: "worker-image-dummy-product" + base: + product: + name: csm + version: "{{csm.version}}" + type: image + filter: + prefix: secure-kubernetes + configuration: "config-minimal-management-dummy-1.0.0" + configuration_group_names: + - Management_Worker + +- name: "master-image-dummy-product" + base: + product: + name: csm + version: "{{csm.version}}" + type: image + filter: + prefix: secure-kubernetes + configuration: "config-minimal-management-dummy-1.0.0" + configuration_group_names: + - Management_Master + +- name: "storage-image-dummy-product" + base: + product: + name: csm + version: "{{csm.version}}" + type: image + filter: + prefix: secure-storage-ceph + configuration: "config-minimal-management-dummy-1.0.0" + configuration_group_names: + - Management_Storage + diff --git a/goss-testing/scripts/iuf_run_setup/product_vars.yaml b/goss-testing/scripts/iuf_run_setup/product_vars.yaml new file mode 100644 index 00000000..3b09d8f0 --- /dev/null +++ b/goss-testing/scripts/iuf_run_setup/product_vars.yaml @@ -0,0 +1,34 @@ +# Copyright 2024 Hewlett Packard Enterprise Development LP +--- + +# variables available for substitution +# -------------------------------------------------------- +# {{name}} - product name from the product manifest +# {{version_x_y}} - x.y portion of the product version +# {{version_x_y_z}} - x.y.z portion of the product version +# {{working_branch}} - default working branch + +# CAUTION +# version substitution in working_branches must be at the very end for automatic updates to +# work properly in the IUF `update-vcs-config` stage. +# ex: integration-{{version_x_y}} (will work correctly) +# ex: integration-{{version_x_y}}-test (will NOT work correctly) + +# set site specific default working_branch with an entry in a default section in site_vars.yaml + +# set default network_type to "mellanox" or "cassini" in the default section in site_vars.yaml + +# set note and/or suffix to a value such as "test" in the default section in site_vars.yaml +# in order to namespace all CFS configurations, images and BOS session templates with that value. + +# CAUTION: note and suffix are used to construct key values and thus spaces must be avoided + +default: + working_branch: "integration-{{version_x_y_z}}" + network_type: "mellanox" + note: "" + suffix: "" + wlm: "future use" + +dummy: + version: 1.0.0 diff --git a/goss-testing/scripts/workflows/iuf_base_workflow.yaml b/goss-testing/scripts/workflows/iuf_base_workflow.yaml new file mode 100644 index 00000000..b2e25300 --- /dev/null +++ b/goss-testing/scripts/workflows/iuf_base_workflow.yaml @@ -0,0 +1,41 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: iuf-base-auto-workflow- + namespace: argo +spec: + arguments: + parameters: + - name: scriptContent + value: | + echo "This is a test script." + - name: dryRun + value: "true" # Set to "false" to execute the script + - name: script_output_file + value: "/tmp/my_output" + workflowTemplateRef: + name: iuf-base-template-auto \ No newline at end of file diff --git a/goss-testing/scripts/workflows/update_cpc_workflow.yaml b/goss-testing/scripts/workflows/update_cpc_workflow.yaml new file mode 100644 index 00000000..696e05b4 --- /dev/null +++ b/goss-testing/scripts/workflows/update_cpc_workflow.yaml @@ -0,0 +1,32 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: update-product-catalog-auto-workflow- + namespace: argo +spec: + workflowTemplateRef: + name: update-product-catalog-template-auto diff --git a/goss-testing/scripts/workflowtemplates/iuf_base_template.yaml b/goss-testing/scripts/workflowtemplates/iuf_base_template.yaml new file mode 100644 index 00000000..7a018b4c --- /dev/null +++ b/goss-testing/scripts/workflowtemplates/iuf_base_template.yaml @@ -0,0 +1,180 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: iuf-base-template-auto + namespace: argo + labels: + version: 4.0.3 +spec: + volumes: + - name: ca-bundle + hostPath: + path: /var/lib/ca-certificates + type: Directory + - name: iuf + hostPath: + path: /etc/cray/upgrade/csm + type: Directory + entrypoint: shell-script + templates: + - name: shell-script + inputs: + parameters: + - name: scriptContent + description: | + The content of the script that will be run + - name: dryRun + description: > + A flag indicating whether to run the script or not. True means do + not run the script merely output it to a file. + - name: script_output_file + value: /tmp/script_output + description: > + The file containing any output the script chooses to write. + Writing to this file is optional. This file's name is stored in + the environment variable SCRIPT_OUTPUT_FILE, which is accessible + to the script. + outputs: + parameters: + - name: output + description: | + Output from the script + valueFrom: + path: '{{inputs.parameters.script_output_file}}' + metadata: + annotations: + sidecar.istio.io/inject: 'false' + retryStrategy: + limit: '1' + retryPolicy: Always + backoff: + duration: 10s + factor: '1' + maxDuration: 1m + script: + image: 'artifactory.algol60.net/csm-docker/stable/iuf:v0.1.12' + command: + - sh + source: > + #!/usr/bin/bash + + ts=$(echo $RANDOM | md5sum | head -c 20; echo) + + cat << 'EOF2' > "/tmp/${ts}.sh" + + set -e + + secret=`kubectl -n argo get serviceaccount/default -o + jsonpath='{.secrets[0].name}'` + + if [[ -z "$secret" ]]; then + token=`kubectl -n argo create token default` + else + token=`kubectl -n argo get secret $secret -o jsonpath='{.data.token}'| base64 -d` + fi + + mkdir -p mykubeconfig + + cat << EOF > mykubeconfig/admin.conf + + apiVersion: v1 + + kind: Config + + current-context: default + + contexts: + - context: + cluster: kubernetes + user: default + namespace: default + name: default + clusters: + - cluster: + server: https://kubeapi-vip.local:6442 + insecure-skip-tls-verify: true + name: kubernetes + users: + + - name: default + user: + token: ${token} + EOF + + export KUBECONFIG=mykubeconfig/admin.conf + + chmod 600 mykubeconfig/admin.conf + + export TOKEN=$(curl -k -s -S -d grant_type=client_credentials \ + -d client_id=admin-client \ + -d client_secret=`kubectl get secrets admin-client-auth -o jsonpath='{.data.client-secret}' | base64 -d` \ + https://api-gw-service-nmn.local/keycloak/realms/shasta/protocol/openid-connect/token | jq -r '.access_token') + API_GW="https://api-gw-service-nmn.local" + + ADMIN_SECRET=$(kubectl get secrets admin-client-auth -o + jsonpath='{.data.client-secret}' | base64 -d) + + curl -k -s -d grant_type=client_credentials \ + -d client_id=admin-client \ + -d client_secret=$ADMIN_SECRET https://api-gw-service-nmn.local/keycloak/realms/shasta/protocol/openid-connect/token > /tmp/setup-token.json + export CRAY_CREDENTIALS=/tmp/setup-token.json + + cray init --hostname $API_GW --no-auth --overwrite > /dev/null + + mkdir -p "$(dirname "{{inputs.parameters.script_output_file}}")" + + touch {{inputs.parameters.script_output_file}} + + {{inputs.parameters.scriptContent}} + + EOF2 + + chmod +x /tmp/${ts}.sh + + DRY_RUN={{inputs.parameters.dryRun}} + + if [[ "$DRY_RUN" == "true" ]];then + echo "=====================" + echo "=======DRY RUN=======" + echo "=====================" + cat /tmp/${ts}.sh + touch {{inputs.parameters.script_output_file}} + else + bash -e /tmp/${ts}.sh + fi + + if [ ! -s {{inputs.parameters.script_output_file}} ]; then + echo "{}" | tee {{inputs.parameters.script_output_file}} + fi + env: + - name: SCRIPT_OUTPUT_FILE + value: '{{inputs.parameters.script_output_file}}' + volumeMounts: + - name: ca-bundle + mountPath: /var/lib/ca-certificates + - name: iuf + mountPath: /etc/cray/upgrade/csm \ No newline at end of file diff --git a/goss-testing/scripts/workflowtemplates/update_product_catalog_template.yaml b/goss-testing/scripts/workflowtemplates/update_product_catalog_template.yaml new file mode 100644 index 00000000..4b4dfca1 --- /dev/null +++ b/goss-testing/scripts/workflowtemplates/update_product_catalog_template.yaml @@ -0,0 +1,67 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: update-product-catalog-template-auto + namespace: argo + labels: + version: "4.0.1" +spec: + entrypoint: catalog-update + templates: + - name: catalog-update + inputs: + parameters: + - name: product-catalog-configmap-name + value: "cray-product-catalog-test" + - name: product-catalog-configmap-namespace + value: "auto" + - name: product-name + value: "test-product" + - name: product-version + value: "1.0.0" + - name: yaml-content + value: "content" + container: + image: artifactory.algol60.net/csm-docker/stable/cray-product-catalog-update:2.0.1 + command: [catalog_update] + env: + - name: PRODUCT + value: "test-product" + - name: PRODUCT_VERSION + value: "1.0.0" + - name: CONFIG_MAP + value: "cray-product-catalog-test" + - name: CONFIG_MAP_NAMESPACE + value: "auto" + - name: YAML_CONTENT_STRING + value: | + key1: value1 + key2: + subkey1: subvalue1 + subkey2: subvalue2 + - name: REMOVE_ACTIVE_FIELD + value: "true" diff --git a/goss-testing/tests/ncn/goss-iuf-activity-abort.yaml b/goss-testing/tests/ncn/goss-iuf-activity-abort.yaml new file mode 100644 index 00000000..5df29295 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-abort.yaml @@ -0,0 +1,58 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $activity := .Vars.iuf_activity }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_abort := $scripts | printf "%s/check_iuf_abort.sh" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_abort" }} + {{$testlabel}}: + title: iuf activity abort + meta: + desc: Executes Abort for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF abort + "{{$iuf_abort}}" + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the iuf abort command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-activity-apis.yaml b/goss-testing/tests/ncn/goss-iuf-activity-apis.yaml new file mode 100644 index 00000000..99425802 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-apis.yaml @@ -0,0 +1,58 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $activity := .Vars.iuf_activity }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_apis := $scripts | printf "%s/python/iuf_apis" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_apis" }} + {{$testlabel}}: + title: iuf apis + meta: + desc: Tests apis access. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF APIS + "{{$iuf_apis}}" "{{ index $activity 0 }}" + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the iuf apis script failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 1800000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-activity-list-stages.yaml b/goss-testing/tests/ncn/goss-iuf-activity-list-stages.yaml new file mode 100644 index 00000000..3b856f87 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-list-stages.yaml @@ -0,0 +1,62 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_stages" }} + {{$testlabel}}: + title: iuf activity stages + meta: + desc: Gives List of stages executed for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF activity + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 0 }}" && \ + + # Running the test case + /usr/bin/iuf -a "{{ index $activity 0 }}" ls + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the iuf ls command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-activity-negative.yaml b/goss-testing/tests/ncn/goss-iuf-activity-negative.yaml new file mode 100644 index 00000000..112e444d --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-negative.yaml @@ -0,0 +1,64 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_negative" }} + {{$testlabel}}: + title: iuf activity info + meta: + desc: Gives data for an IUF activity which does not exist + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF activity + /usr/bin/iuf -a "{{ index $activity 2 }}" activity + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with non-zero exit code if the iuf activity command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + stdout: + - "+------------------------------------------------------------------------------------+" + - "| Activity: {{ index $activity 2 }} [local only] |" + - "+-----------------+----------+-------------------------+--------+----------+---------+" + - "| Start / Session | Category | Command / Argo Workflow | Status | Duration | Comment |" + - "+-----------------+----------+-------------------------+--------+----------+---------+" + - "+-----------------+----------+-------------------------+--------+----------+---------+" + timeout: 20000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-activity-restart.yaml b/goss-testing/tests/ncn/goss-iuf-activity-restart.yaml new file mode 100644 index 00000000..f5f9c889 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-restart.yaml @@ -0,0 +1,61 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $activity := .Vars.iuf_activity }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_abort := $scripts | printf "%s/check_iuf_abort.sh" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_restart" }} + {{$testlabel}}: + title: iuf activity restart + meta: + desc: Executes restart for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run prechecks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run an IUF session and abort it + "{{$iuf_abort}}" && \ + + # Restart the aborted activity + /usr/bin/iuf -a test-activity restart + + # Capture the exit code of the previous step + run_exit_code=$? + + # Cleanup the workflows,log and media dir + "{{$iuf_cleanup}}" + + # Exit with non-zero exit code if the iuf command failed with exit non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-activity-resume.yaml b/goss-testing/tests/ncn/goss-iuf-activity-resume.yaml new file mode 100644 index 00000000..dad9cb28 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity-resume.yaml @@ -0,0 +1,59 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_abort := $scripts | printf "%s/check_iuf_abort.sh" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_resume" }} + {{$testlabel}}: + title: iuf activity resume + meta: + desc: Executes resume for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run prechecks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run an IUF session and abort it + "{{$iuf_abort}}" && \ + sleep 30 && \ + /usr/bin/iuf -a test-activity resume + + # Capture the exit code of the previous step + run_exit_code=$? + + # Cleanup the workflows,log and media dir + "{{$iuf_cleanup}}" + + # Exit with non-zero exit code if the iuf command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-activity.yaml b/goss-testing/tests/ncn/goss-iuf-activity.yaml new file mode 100644 index 00000000..5a2042de --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-activity.yaml @@ -0,0 +1,66 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_post_checks := $scripts | printf "%s/python/iuf_post_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity" }} + {{$testlabel}}: + title: iuf activity info + meta: + desc: Gives data for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 0 }}" && \ + + # TEST CASE: iuf activity + /usr/bin/iuf -a "{{ index $activity 0 }}" activity + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run post-checks for IUF + "{{$iuf_post_checks}}" "{{ index $activity 0 }}" + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the iuf activity failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-activity-abort.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-activity-abort.yaml new file mode 100644 index 00000000..5e595ba7 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-activity-abort.yaml @@ -0,0 +1,57 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $activity := .Vars.iuf_activity }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_invalid_activity_abort" }} + {{$testlabel}}: + title: iuf invalid activity abort + meta: + desc: Executes Abort for a non existing IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf -a "{{ index $activity 2 }}" abort + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with non-zero exit code if the iuf abort command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-activity-restart.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-activity-restart.yaml new file mode 100644 index 00000000..ec98b201 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-activity-restart.yaml @@ -0,0 +1,57 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_restart_on_non_existing_activity" }} + {{$testlabel}}: + title: iuf restart on non existing activity + meta: + desc: Run IUF restart on non existing activity . + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf -a "{{ index $activity 2 }}" restart + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with non-zero exit code if the iuf restart command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 20000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-activity-resume.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-activity-resume.yaml new file mode 100644 index 00000000..bab69557 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-activity-resume.yaml @@ -0,0 +1,57 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_resume_on_non_existing_activity" }} + {{$testlabel}}: + title: iuf resume on non existing activity + meta: + desc: Run IUF resume on non existing activity . + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf -a "{{ index $activity 2 }}" resume + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with non-zero exit code if the iuf resume command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 50000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-activity.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-activity.yaml new file mode 100644 index 00000000..f732f29b --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-activity.yaml @@ -0,0 +1,62 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_invalid_activity_name" }} + {{$testlabel}}: + title: invalid activity name for IUF run + meta: + desc: The output of IUF if invalid activity name is used. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + + # Run pre-checks + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run the main IUF run step and check for exit code 1 + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 1 }}" + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step independently + "{{$iuf_cleanup}}" "{{ index $activity 1 }}" + + # Exit with exit code 1 if the iuf_run command failed with non-zero exit code + if [ $run_exit_code -ne 0 ]; then + exit 1 + fi + ' + exit-status: 1 + timeout: 180000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-log-dir.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-log-dir.yaml new file mode 100644 index 00000000..faabd19f --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-log-dir.yaml @@ -0,0 +1,62 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $logdir := .Vars.iuf_logdir }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_run_invalid_log_dir" }} + {{$testlabel}}: + title: iuf run_invalid_log_dir + meta: + desc: Run IUF with invalid log dir. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run the main IUF run step and check for exit code 1 + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 2 }}" "{{ index $logdir 0 }}" + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with exit code 1 if the iuf_run command failed with non-zero exit code + if [ $run_exit_code -ne 0 ]; then + exit 1 + fi + ' + exit-status: 1 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-invalid-media.yaml b/goss-testing/tests/ncn/goss-iuf-invalid-media.yaml new file mode 100644 index 00000000..2ced1918 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-invalid-media.yaml @@ -0,0 +1,60 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := "/etc/cray/upgrade/csm/non_existing_media_dir" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_invalid_media_dir" }} + {{$testlabel}}: + title: iuf run with invalid media + meta: + desc: Run IUF with a media-dir which does not exist. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF activity + /usr/bin/iuf -a "{{ index $activity 2 }}" -m "{{$media_dir}}" run -rv "{{$media_dir}}/product_vars.yaml" -r process-media + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with exit code 1 if the iuf command failed with non-zero exit code + if [ $run_exit_code -ne 0 ]; then + exit 1 + fi + ' + exit-status: 1 + timeout: 50000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-list-activities.yaml b/goss-testing/tests/ncn/goss-iuf-list-activities.yaml new file mode 100644 index 00000000..9387480c --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-list-activities.yaml @@ -0,0 +1,46 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +command: + {{ $testlabel := "iuf_list_activities" }} + {{$testlabel}}: + title: IUF list-activities/la + meta: + desc: Lists all the IUF activities on the cluster. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf la + ' + exit-status: 0 + timeout: 20000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-list-activity-workflows.yaml b/goss-testing/tests/ncn/goss-iuf-list-activity-workflows.yaml new file mode 100644 index 00000000..c58b9615 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-list-activity-workflows.yaml @@ -0,0 +1,62 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $activity := .Vars.iuf_activity }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_activity_workflows" }} + {{$testlabel}}: + title: iuf activity workflows + meta: + desc: Gives List of workflows for a particular IUF activity. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Setup and IUF run + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 0 }}" && \ + + # Test IUF list workflow command + /usr/bin/iuf -a "{{ index $activity 0 }}" workflow + + # Capture the exit code of the previous step + run_exit_code=$? + + # Cleanup the created activity workflow, log, and media directory + "{{$iuf_cleanup}}" + + # Exit with non-zero exit code if the iuf -a workflow command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-list-stages-negative.yaml b/goss-testing/tests/ncn/goss-iuf-list-stages-negative.yaml new file mode 100644 index 00000000..fcec8bed --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-list-stages-negative.yaml @@ -0,0 +1,73 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_list_stages_negative" }} + {{$testlabel}}: + title: iuf list stages/ls + meta: + desc: Lists stages for an IUF activity which does not exist + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf -a "{{ index $activity 2 }}" ls + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with non-zero exit code if the iuf -a ls command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + stdout: + - "+----------------------------+---------------------------------------------------------------------------------------+--------+----------+" + - "| Stage | Description | Status | Duration |" + - "+----------------------------+---------------------------------------------------------------------------------------+--------+----------+" + - "| process-media | Inventory and extract products in the media directory for use in subsequent stages | N/A | N/A |" + - "| pre-install-check | Perform pre-install readiness checks | N/A | N/A |" + - "| deliver-product | Upload product content onto the system | N/A | N/A |" + - "| update-vcs-config | Merge working branches and perform automated VCS configuration | N/A | N/A |" + - "| update-cfs-config | Update CFS configuration utilizing sat bootprep | N/A | N/A |" + - "| prepare-images | Build and configure management node and/or managed node images utilizing sat bootprep | N/A | N/A |" + - "| management-nodes-rollout | Rolling rebuild of management nodes | N/A | N/A |" + - "| deploy-product | Deploy services to system | N/A | N/A |" + - "| post-install-service-check | Perform post-install checks of deployed product services | N/A | N/A |" + - "| managed-nodes-rollout | Rolling reboot of managed nodes | N/A | N/A |" + - "| post-install-check | Perform post-install checks | N/A | N/A |" + - "+----------------------------+---------------------------------------------------------------------------------------+--------+----------+" + timeout: 20000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-list-stages.yaml b/goss-testing/tests/ncn/goss-iuf-list-stages.yaml new file mode 100644 index 00000000..6ca8e944 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-list-stages.yaml @@ -0,0 +1,46 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} + +command: + {{ $testlabel := "iuf_list_stages" }} + {{$testlabel}}: + title: IUF list-stages/ls + meta: + desc: Lists all the stages of IUF. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF ls command + echo "Running iuf list-stages test case" && \ + /usr/bin/iuf ls + ' + exit-status: 0 + timeout: 20000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-no-media.yaml b/goss-testing/tests/ncn/goss-iuf-no-media.yaml new file mode 100644 index 00000000..334ba173 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-no-media.yaml @@ -0,0 +1,58 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := "/etc/cray/upgrade/csm/dummy" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_no_media_dir" }} + {{$testlabel}}: + title: iuf run without media + meta: + desc: Run IUF without media-dir . + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + /usr/bin/iuf -a "{{ index $activity 2 }}" run -rv "{{$media_dir}}/product_vars.yaml" -r process-media + run_exit_code=$? + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 2 }}" + + # Exit with exit code 1 if the iuf command failed with non-zero exit code + if [ $run_exit_code -ne 0 ]; then + exit 1 + fi + ' + exit-status: 1 + timeout: 50000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-run.yaml b/goss-testing/tests/ncn/goss-iuf-run.yaml new file mode 100644 index 00000000..18dc06e1 --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-run.yaml @@ -0,0 +1,59 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_run" }} + {{$testlabel}}: + title: iuf run + meta: + desc: Run IUF with dummy product. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Run IUF + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 0 }}" + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the iuf_run command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-specified-log-dir.yaml b/goss-testing/tests/ncn/goss-iuf-specified-log-dir.yaml new file mode 100644 index 00000000..cd02264c --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-specified-log-dir.yaml @@ -0,0 +1,66 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $logdir := .Vars.iuf_logdir }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_post_checks := $scripts | printf "%s/python/iuf_post_checks" }} +{{ $iuf_run := $scripts | printf "%s/python/iuf_run" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_run_specified_log_dir" }} + {{$testlabel}}: + title: iuf run_specified_log_dir + meta: + desc: Run IUF with specified log dir. + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + mkdir "{{ index $logdir 1}}" && \ + + # Run IUF + "{{$iuf_run}}" "{{$media_dir}}" "{{ index $activity 0 }}" "{{ index $logdir 1}}" && \ + + # Capture the exit code of the previous step + run_exit_code=$? + + # Run post-checks for IUF + "{{$iuf_post_checks}}" "{{ index $activity 0 }}" + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" && \ + rm -r "{{ index $logdir 1}}" + + # Exit with non-zero exit code if the iuf_run command failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 180000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-stages.yaml b/goss-testing/tests/ncn/goss-iuf-stages.yaml new file mode 100644 index 00000000..42e3e9db --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-stages.yaml @@ -0,0 +1,62 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $activity := .Vars.iuf_activity }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $media_dir := .Env.GOSS_BASE | printf "%s/scripts/iuf_run_setup" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $iuf_stages := $scripts | printf "%s/python/iuf_stages" }} +{{ $iuf_stages_cleanup := $scripts | printf "%s/python/iuf_stages_cleanup" }} +{{ $iuf_cleanup := $scripts | printf "%s/python/iuf_cleanup" }} +command: + {{ $testlabel := "iuf_stages" }} + {{$testlabel}}: + title: iuf stages + meta: + desc: Executes IUF from process-media to prepare-images with dummy product + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # TEST CASE: Run IUF stages + "{{$iuf_stages}}" "{{$media_dir}}" "{{ index $activity 0 }}" + + # Capture the exit code of the previous step + run_exit_code=$? + + "{{$iuf_stages_cleanup}}" + + # Run cleanup step + "{{$iuf_cleanup}}" "{{ index $activity 0 }}" + + # Exit with non-zero exit code if the stage operations script failed with non-zero exit code + exit $run_exit_code + ' + exit-status: 0 + timeout: 2700000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-iuf-validate-iuf-manifest.yaml b/goss-testing/tests/ncn/goss-iuf-validate-iuf-manifest.yaml new file mode 100644 index 00000000..a5dcc1bb --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-validate-iuf-manifest.yaml @@ -0,0 +1,42 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $validate_iuf_manifest := .Env.GOSS_BASE | printf "%s/scripts/python/validate_iuf_manifest" }} +{{ $manifest := .Vars.iuf_manifest }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} + +command: + {{ $testlabel := "iuf_validate_product_manifest" }} + {{$testlabel}}: + title: iuf validate product_manifest + meta: + desc: Validates iuf_product_manifest.yaml + sev: 0 + exec: |- + # validate iuf product manifest + "{{$logrun}}" -l "{{$testlabel}}" "{{ $validate_iuf_manifest }}" "{{ index $manifest 0 }}" + exit-status: 0 + timeout: 180000 + skip: false diff --git a/goss-testing/tests/ncn/goss-iuf-workflowtemplates.yaml b/goss-testing/tests/ncn/goss-iuf-workflowtemplates.yaml new file mode 100644 index 00000000..6af8741b --- /dev/null +++ b/goss-testing/tests/ncn/goss-iuf-workflowtemplates.yaml @@ -0,0 +1,74 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $pre_checks := .Vars.iuf_pre_checks }} +{{ $template_images := .Vars.iuf_workflowtemplate_images }} +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} +{{ $iuf_pre_checks := $scripts | printf "%s/python/iuf_pre_checks" }} +{{ $workflows_dir := .Env.GOSS_BASE | printf "%s/scripts/workflows" }} +{{ $workflowtemplates_dir := .Env.GOSS_BASE | printf "%s/scripts/workflowtemplates" }} +{{ $check_workflows := $scripts | printf "%s/python/check_workflow_template" }} + +command: + {{ $testlabel := "iuf_worklfowtemplates" }} + {{$testlabel}}: + title: iuf worklfow-templates + meta: + desc: Executes two IUF workflowtemplates + sev: 0 + exec: |- + "{{$logrun}}" -l "{{$testlabel}}" bash -c ' + # Run pre-checks for IUF + "{{$iuf_pre_checks}}" "{{ index $pre_checks 0 }}" "{{ index $pre_checks 1 }}" "{{ index $pre_checks 2 }}" "{{ index $pre_checks 3 }}" "{{ index $pre_checks 4 }}" "{{ index $pre_checks 5 }}" && \ + + # Setting up namespace and copying CPC configmap + kubectl create namespace auto --dry-run=client -o yaml | kubectl apply -f - && \ + kubectl get configmap cray-product-catalog -n services -o yaml | \ + sed "s/name: cray-product-catalog/name: cray-product-catalog-test/" | \ + sed "s/namespace: services/namespace: auto/" | kubectl apply -n auto -f - && \ + + # TEST CASE: Run IUF CPC template + "{{$check_workflows}}" "{{$workflowtemplates_dir}}/update_product_catalog_template.yaml" "{{$workflows_dir}}/update_cpc_workflow.yaml" "{{ index $template_images 0 }}" && \ + + # Capture the exit code of the previous step + run_exit_code_1=$? + + # TEST CASE: Run iuf base template + "{{$check_workflows}}" "{{$workflowtemplates_dir}}/iuf_base_template.yaml" "{{$workflows_dir}}/iuf_base_workflow.yaml" "{{ index $template_images 1 }}" + + # Capture the exit code of the previous step + run_exit_code_2=$? + + # Cleanup: Delete the configmap and namespace + kubectl delete configmap cray-product-catalog-test -n auto && \ + kubectl delete namespace auto + + # Fail if either subtest failed with non-zero exit code + [[ $run_exit_code_1 -ne 0 ]] && exit $run_exit_code_1 + exit $run_exit_code_2 + ' + exit-status: 0 + timeout: 1800000 + skip: false \ No newline at end of file diff --git a/goss-testing/tests/ncn/goss-sat-validate-compute-uan-bootprep.yaml b/goss-testing/tests/ncn/goss-sat-validate-compute-uan-bootprep.yaml new file mode 100644 index 00000000..b271d7b5 --- /dev/null +++ b/goss-testing/tests/ncn/goss-sat-validate-compute-uan-bootprep.yaml @@ -0,0 +1,42 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $sat_validate_bootprep := .Env.GOSS_BASE | printf "%s/scripts/python/sat_validate_bootprep" }} +{{ $bootprep := .Vars.bootprep }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} + +command: + {{ $testlabel := "sat_validate_compute_uan_bootprep" }} + {{$testlabel}}: + title: iuf validate compute and uan bootprep + meta: + desc: Validates compute-and-uan-bootprep.yaml + sev: 0 + exec: |- + # validate compute-and-uan-bootprep + "{{$logrun}}" -l "{{$testlabel}}" "{{ $sat_validate_bootprep }}" "{{ index $bootprep 1 }}" + exit-status: 0 + timeout: 20000 + skip: false diff --git a/goss-testing/tests/ncn/goss-sat-validate-management-bootprep.yaml b/goss-testing/tests/ncn/goss-sat-validate-management-bootprep.yaml new file mode 100644 index 00000000..32feb4e8 --- /dev/null +++ b/goss-testing/tests/ncn/goss-sat-validate-management-bootprep.yaml @@ -0,0 +1,42 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +{{ $scripts := .Env.GOSS_BASE | printf "%s/scripts" }} +{{ $sat_validate_bootprep := .Env.GOSS_BASE | printf "%s/scripts/python/sat_validate_bootprep" }} +{{ $bootprep := .Vars.bootprep }} +{{ $logrun := .Env.GOSS_BASE | printf "%s/scripts/log_run.sh" }} + +command: + {{ $testlabel := "sat_validate_management_bootprep" }} + {{$testlabel}}: + title: iuf validate management bootprep + meta: + desc: Validates management-bootprep.yaml + sev: 0 + exec: |- + # validate management-bootprep + "{{$logrun}}" -l "{{$testlabel}}" "{{ $sat_validate_bootprep }}" "{{ index $bootprep 0 }}" + exit-status: 0 + timeout: 20000 + skip: false diff --git a/goss-testing/vars/variables-ncn.yaml b/goss-testing/vars/variables-ncn.yaml index b5babef3..2c366a30 100644 --- a/goss-testing/vars/variables-ncn.yaml +++ b/goss-testing/vars/variables-ncn.yaml @@ -163,3 +163,31 @@ storage_fs_labels: pit_node: false # false by default--overriden when vshasta is deployed vshasta: false + +iuf_pre_checks: + - v1.22.13 + - "iuf-cli-1.6.11-1.x86_64" + - "starlord" + - "4.0.12" + - "4.0.13" + - "4.0.13" + +iuf_activity: + - "test-activity" + - "INVALID_ACTIVITY" + - "new-activity-auto" + +iuf_logdir: + - "/etc/cray/upgrade/csm/non_existing_directory" + - "/etc/cray/upgrade/csm/csm-testing" + +iuf_manifest: + - "/etc/cray/upgrade/csm/iuf-product-manifest.yaml" + +bootprep: + - "/etc/cray/upgrade/csm/management-bootprep.yaml" + - "/etc/cray/upgrade/csm/compute-and-uan-bootprep.yaml" + +iuf_workflowtemplate_images: + - "2.4.1" + - "v0.1.12" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 75f1da39..3e157c83 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,10 @@ kubernetes PyYAML requests requests-retry-session +requests-oauthlib +packaging +python-keycloak +jsonschema urllib3 # There are some additional Python packages that test scripts can rely on that are diff --git a/src/csm_testing/lib/iuf_classes.py b/src/csm_testing/lib/iuf_classes.py new file mode 100644 index 00000000..1c0e4a58 --- /dev/null +++ b/src/csm_testing/lib/iuf_classes.py @@ -0,0 +1,417 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +Defining class for API calls +""" +import os +import base64 +from urllib.error import HTTPError +from typing import Tuple +import requests +from kubernetes import client, config +from keycloak import KeycloakOpenID + + +class AuthException(Exception): + """A wrapper for raising an AuthException exception.""" + + +class Auth: + """Class to get the k8s secret and generate token for making API calls""" + + def __init__(self): + self._token = None + + @classmethod + def get_secrets(cls) -> Tuple[str, str]: + """Reads secret for generating token + + Raises: + AuthException + + Returns: + Tuple[str, str]: token + """ + try: + config.load_kube_config() + k8s_v1 = client.CoreV1Api() + sec = k8s_v1.read_namespaced_secret("admin-client-auth", + "default").data + username = base64.b64decode( + sec.get("client-id").strip()).decode("utf-8") + password = base64.b64decode( + sec.get("client-secret").strip()).decode("utf-8") + except Exception as err: + raise AuthException( + "Unable to load secrets from Kubernetes") from err + + return username, password + + @classmethod + def get_token(cls, username, password): + """Gets token for API calls + + Args: + username (str): username from the secret + password (str): password from the secret + + Raises: + AuthException + + Returns: + token + """ + try: + keycloak_openid = KeycloakOpenID( + server_url="https://api-gw-service-nmn.local/keycloak/", + client_id=username, + realm_name="shasta", + client_secret_key=password, + verify=False, + ) + + token = keycloak_openid.token(grant_type="client_credentials") + except Exception as err: + raise AuthException( + "Unable to obtain token from Keycloak") from err + + return token["access_token"] + + @property + def token(self): + """Gets the token and update it in self._token + + Returns: + str: token + """ + if not self._token: + username, password = self.get_secrets() + self._token = self.get_token(username, password) + + return self._token + + +class ApiInterface: + """Class contains all the API call functions""" + + def __init__( + self, + apiurl: str = "https://api-gw-service-nmn.local/apis", + resource: str = "/iuf/v1", + ): + self.auth = Auth() + self.apiurl = os.getenv("IUF_API_URL", apiurl) + self.resource = os.getenv("IUF_API_URL_RESOURCE", resource) + + def request(self, method, path, payload=None, timeout=None): + """Makes API request + + Args: + method (str): method for the API request + path (str): endpoint for the url + payload : Defaults to None. + timeout (int, optional): timeout for the request. Defaults to None. + + Returns: + API response + """ + method = method.upper() + assert method in [ + "GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS" + ] + + url = self.apiurl + self.resource + path + + headers = {} + try: + token = self.auth.token + headers["Authorization"] = f"Bearer {token}" + except AuthException: + if "gw-service" in self.apiurl: + raise + + method_func = method.lower() + try: + if payload: + result = getattr(requests, method_func)(url, + headers=headers, + json=payload, + verify=False, + timeout=timeout) + else: + result = getattr(requests, method_func)(url, + headers=headers, + verify=False, + timeout=timeout) + except Exception as err: + print(err) + raise + + # throw an exception for bad status codes + result.raise_for_status() + + return result + + def activity_exists(self, activity): + """Checks if the activity exists + + Args: + activity (activity): name of IUF activity + + Returns: + bool: If API call is successful then True, False otherwise + """ + try: + self.get_activity(activity) + return True + except Exception: + return False + + def get_stages(self): + """Gets all IUF stages + + Returns: + API response containing list of IUF stages + """ + api_path = "/stages" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activity(self, activity): + """Fetch details of a specific activity. + + Args: + activity (str): Name of the IUF activity. + + Returns: + API response containing activity details. + """ + api_path = f"/activities/{activity}" + + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activities(self): + """Fetch a list of all activities. + + Returns: + API response containing a list of activities. + """ + api_path = "/activities" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activity_sessions(self, activity): + """Fetch sessions for a specific activity. + + Args: + activity (str): Name of the IUF activity. + + Returns: + API response containing activity sessions. + """ + api_path = f"/activities/{activity}/sessions" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def post_activity(self, payload): + """Create a new activity. + + Args: + payload (dict): Data required to create the activity. + + Returns: + API response after creating the activity. + """ + api_path = "/activities" + + try: + api_response = self.request("POST", api_path, payload) + return api_response + except HTTPError as err: + print(err) + raise + + def patch_activity(self, activity, payload): + """Update an existing activity. + + Args: + activity (str): Name of the IUF activity. + payload (dict): Data required to update the activity. + + Returns: + API response after updating the activity. + """ + api_path = f"/activities/{activity}" + + try: + api_response = self.request("PATCH", api_path, payload) + return api_response + except HTTPError as err: + print(err) + raise + + def abort_activity(self, activity, payload): + """Abort a specific activity. + + Args: + activity (str): Name of the IUF activity. + payload (dict): Data required to abort the activity. + + Returns: + API response after aborting the activity. + """ + api_path = f"/activities/{activity}/history/abort" + try: + api_response = self.request("POST", api_path, payload, timeout=90) + return api_response + except requests.ReadTimeout as exc: + raise exc + except HTTPError as err: + print(err) + raise + + def post_activity_history_run(self, activity, payload): + """Run an activity and store it in history. + + Args: + activity (str): Name of the IUF activity. + payload (dict): Data required to run the activity. + + Returns: + API response after running the activity. + """ + api_path = f"/activities/{activity}/history/run" + + try: + api_response = self.request("POST", api_path, payload) + return api_response + except HTTPError as err: + print(err) + raise + + def post_resume(self, activity, payload): + """Resume a specific activity. + + Args: + activity (str): Name of the IUF activity. + payload (dict): Data required to resume the activity. + + Returns: + API response after resuming the activity. + """ + api_path = f"/activities/{activity}/history/resume" + try: + api_response = self.request("POST", api_path, payload) + return api_response + except HTTPError as err: + print(err) + raise + + def post_restart(self, activity, payload): + """Restart a specific activity. + + Args: + activity (str): Name of the IUF activity. + payload (dict): Data required to restart the activity. + + Returns: + API response after restarting the activity. + """ + api_path = f"/activities/{activity}/history/restart" + try: + api_response = self.request("POST", api_path, payload) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activity_history(self, activity): + """Fetch the history of a specific activity. + + Args: + activity (str): Name of the IUF activity. + + Returns: + API response containing the activity history. + """ + api_path = f"/activities/{activity}/history" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activity_history_time(self, activity, time): + """Fetch the history of an activity at a specific time. + + Args: + activity (str): Name of the IUF activity. + time (str): Specific time for fetching activity history. + + Returns: + API response containing the activity history at the specified time. + """ + api_path = f"/activities/{activity}/history/{time}" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise + + def get_activity_session(self, activity, session_name): + """Fetch details of a specific activity session. + + Args: + activity (str): Name of the IUF activity. + session_name (str): Name of the activity session. + + Returns: + API response containing session details. + """ + api_path = f"/activities/{activity}/sessions/{session_name}" + try: + api_response = self.request("GET", api_path) + return api_response + except HTTPError as err: + print(err) + raise diff --git a/src/csm_testing/lib/iuf_common.py b/src/csm_testing/lib/iuf_common.py new file mode 100644 index 00000000..d0a21af8 --- /dev/null +++ b/src/csm_testing/lib/iuf_common.py @@ -0,0 +1,155 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +The script has common functions being used by iuf tests scripts +""" + +import os +import shutil +import sys +import subprocess +import base64 +from requests.auth import HTTPBasicAuth +from kubernetes import client, config +from csm_testing.lib.iuf_constants import MEDIA_DIR, NAMESPACE + +FOLDER_NAME = "dummy-1.0.0" + + +def run_command(command): + """ + Function to run a given command + Args: + command(str): Command to be executed + Returns: + None + """ + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + return result.stdout.decode().strip(), result.returncode + except subprocess.CalledProcessError as err: + print(f"ERROR: Command failed with error: {err}") + sys.exit(1) + + +def media_dir_setup(tar_dir): + """Function to setup the media directory + + Args: + tar_dir (str): path of the tar file for media directory + """ + try: + os.makedirs(MEDIA_DIR, exist_ok=True) + print(f"INFO: Directory {MEDIA_DIR} created successfully.") + except OSError as err: + print(f"ERROR: Unable to create directory {MEDIA_DIR}: {err}") + sys.exit(1) + + # Copying product tar file and manifest files into the media directory + try: + shutil.copy(f"{tar_dir}/{FOLDER_NAME}.tar.gz", MEDIA_DIR) + shutil.copy(f"{tar_dir}/product_vars.yaml", MEDIA_DIR) + shutil.copy(f"{tar_dir}/management-bootprep.yaml", MEDIA_DIR) + print(f"INFO: Files copied successfully to {MEDIA_DIR}.") + except IOError as err: + print(f"ERROR: Failed while copying files: {err}") + sys.exit(1) + + +def get_nexus_credentials(namespace="nexus", + secret_name="nexus-admin-credential"): + """Retrieve Nexus credentials from a Kubernetes secret. + Args: + namespace(str): Namespace. Defaults to 'nexus' + secret_name(str): Secret Name. Defaults to 'nexus-admin-credential' + Returns: + None + """ + # Check if the secret exists + run_command(f"kubectl get secret -n {namespace} {secret_name}") + + # Retrieve and decode credentials + username_base64, _ = run_command( + f"kubectl get secret -n {namespace} {secret_name} --template={{{{.data.username}}}}" + ) + password_base64, _ = run_command( + f"kubectl get secret -n {namespace} {secret_name} --template={{{{.data.password}}}}" + ) + + if username_base64 and password_base64: + username = base64.b64decode(username_base64).decode() + password = base64.b64decode(password_base64).decode() + return username, password + sys.exit("ERROR: Failed to retrieve credentials") + + +def get_ca_certificates(namespace, cert_configmap_name): + """ + Retrieve CA certificates from the specified ConfigMap + Args: + namespace(str): Namespace + cert_configmap_name(str): certificate configmap name + Returns: + None + """ + config.load_kube_config() + + v1 = client.CoreV1Api() + configmap = v1.read_namespaced_config_map(cert_configmap_name, namespace) + + ca_cert_path = "/tmp/ca.crt" + with open(ca_cert_path, "w", encoding="utf-8") as file: + file.write(configmap.data["certificate_authority.crt"]) + + return ca_cert_path + + +def vcs_auth(): + """ + Function for VCS authentication + """ + user_cmd = ("kubectl get secret -n services vcs-user-credentials " + "--template={{.data.vcs_username}} | base64 --decode") + + pass_cmd = ("kubectl get secret -n services vcs-user-credentials " + "--template={{.data.vcs_password}} | base64 --decode") + vcs_user, _ = run_command(user_cmd) + vcs_password, _ = run_command(pass_cmd) + + configmap_name = "cray-configmap-ca-public-key" + gitea_base_url = "https://api-gw-service-nmn.local/vcs" + gitea_url = f'{gitea_base_url.rstrip("/")}/api/v1' + org = "cray" + repo_name = "dummy-config-management" + + ca_cert_path = get_ca_certificates(NAMESPACE, configmap_name) + auth = HTTPBasicAuth(vcs_user, vcs_password) + dummy_repo_url = f"{gitea_url}/repos/{org}/{repo_name}" + + return dummy_repo_url, auth, ca_cert_path diff --git a/src/csm_testing/lib/iuf_constants.py b/src/csm_testing/lib/iuf_constants.py new file mode 100644 index 00000000..ecd6bdc4 --- /dev/null +++ b/src/csm_testing/lib/iuf_constants.py @@ -0,0 +1,32 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +constants being used by iuf scripts +""" + +MEDIA_DIR = "/etc/cray/upgrade/csm/automation-tests" +NAMESPACE = "services" +CONFIGMAP_NAME = "cray-product-catalog" +PRODUCT_NAME = "dummy" +PRODUCT_VERSION = "1.0.0" +NEXUS_URL = "https://packages.local" diff --git a/src/csm_testing/tests/check_workflow_template/__init__.py b/src/csm_testing/tests/check_workflow_template/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/check_workflow_template/__main__.py b/src/csm_testing/tests/check_workflow_template/__main__.py new file mode 100644 index 00000000..dd1162bc --- /dev/null +++ b/src/csm_testing/tests/check_workflow_template/__main__.py @@ -0,0 +1,375 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script checks workflow templates. +""" + +import json +import subprocess +import sys +import time +from kubernetes import client, config +import yaml + + +def is_main_container_finished(pod_name: str, namespace: str) -> bool: + """ + Function to check if the main container is terminated + Args: + pod_name(str): Pod Name + namespace(str): Pod Namespace + Returns: + Boolean True: if the main container is terminated + Boolean False: if the main container is still running + """ + k8s_v1 = client.CoreV1Api() + pod = k8s_v1.read_namespaced_pod(name=pod_name, namespace=namespace) + + for container_status in pod.status.container_statuses: + if container_status.name == "main": + if container_status.state.terminated: + return True + return False + + +def check_and_kill_pod(workflow_name: str, namespace: str) -> bool: + """ + Kills the pod if the main container is finished + Args: + worlflow_name(str): Name of the workflow + namespace(str): Namespace of the workflow + Returns: + Boolean True: if the pod with terminated main container is deleted + Boolean False: if the main container is still running in the pod + """ + k8s_v1 = client.CoreV1Api() + + pods = k8s_v1.list_namespaced_pod( + namespace=namespace, + label_selector=f"workflows.argoproj.io/workflow={workflow_name}", + ).items + + for pod in pods: + pod_name = pod.metadata.name + print(f"INFO: Checking pod: {pod_name}") + + # Check if the main container has finished + if is_main_container_finished(pod_name, namespace): + print( + f"INFO: Main container finished in pod {pod_name}. Deleting pod." + ) + k8s_v1.delete_namespaced_pod(name=pod_name, namespace=namespace) + print(f"INFO: Pod {pod_name} deleted.") + return True + print(f"INFO: Main container still running in pod {pod_name}.") + return False + + +def update_image_version_in_template( + workflow_template_str: str, + old_image_prefix: str, + old_version: str, + new_version: str, +): + """Find the image in the workflowtemplate and replace its version + with the user provided version + + Args: + workflow_template_str (str): workflow template + old_image_prefix (str): old image + old_version (str): old image version + new_version (str): new image version + + Returns: + str: updated workflow template + """ + updated_workflow_template_str = workflow_template_str.replace( + old_image_prefix + old_version, old_image_prefix + new_version) + return updated_workflow_template_str + + +def wait_for_workflow_to_succeed(namespace: str, + workflow_name: str, + timeout=6000, + interval=20): + """ + Polls the workflow status until it succeeds or the timeout is reached. + Args: + namespace(str): Namespace of the workflow + workflow_name(str): Workflow name + timeout(int): Timeout duration + interval(int): Interval duration + Returns: + Boolean: If workflow succeeds returns True,False otherwise + """ + start_time = time.time() + while time.time() - start_time < timeout: + try: + # Get the workflow status + command = [ + "kubectl", + "get", + "workflow", + workflow_name, + "-n", + namespace, + "-o", + "jsonpath={.status.phase}", + ] + result = subprocess.run( + command, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + except subprocess.CalledProcessError as err: + print(f"ERROR: Exception when fetching workflow status: {err}") + return False + + status = result.stdout.strip() + + # Check the status of the workflow + print(f"Workflow {workflow_name} status: {status}") + + if status == "Succeeded": + print(f"INFO: Workflow {workflow_name} succeeded.") + return True + + if status in ["Failed", "Error"]: + print( + f"ERROR: Workflow {workflow_name} failed with status {status}." + ) + return False + + if status == "Running": + print("INFO: Check if main is done") + if check_and_kill_pod(workflow_name, namespace): + print( + f"WARNING: {workflow_name} is running but the main container is done." + ) + return True + return False + + # Wait before polling again + time.sleep(interval) + + print( + f"ERROR: Workflow {workflow_name} did not complete within {timeout} seconds." + ) + return False + + +def delete_resources(namespace, workflow_name, workflow_template_name): + """ + Delete the workflow, workflow template, and associated pods. + Args: + namespace(str): Namespace name + workflow_name(str): Workflow name + workflow_template_name(str): Workflow template name + Returns: + None + """ + + for resource, name in [ + ("workflow", workflow_name), + ("workflowtemplate", workflow_template_name), + ]: + try: + print(f"INFO: Deleting {resource} {name}...") + subprocess.run( + ["kubectl", "delete", resource, name, "-n", namespace], + check=True) + print( + f"INFO: {resource.capitalize()} {name} deleted successfully.") + except subprocess.CalledProcessError as err: + print(f"WARNING: Failed to delete {resource} {name}: {err}") + continue + + try: + print(f"INFO: Finding pods owned by workflow {workflow_name}...") + pods_json = subprocess.run( + ["kubectl", "get", "pods", "-n", namespace, "-o", "json"], + check=True, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + except subprocess.CalledProcessError as err: + print( + f"WARNING: Failed to retrieve pod list for workflow {workflow_name}: {err}" + ) + return + try: + pod_names = [ + pod["metadata"]["name"] + for pod in json.loads(pods_json).get("items", []) if any( + owner.get("name") == workflow_name for owner in pod.get( + "metadata", {}).get("ownerReferences", [])) + ] + + except json.JSONDecodeError as err: + print(f"WARNING: Failed to parse pod list: {err}") + return + + # Delete the found pods + if not pod_names: + print(f"INFO: No pods found for workflow {workflow_name}.") + return + + for pod_name in pod_names: + try: + print(f"INFO: Deleting pod {pod_name}...") + subprocess.run( + ["kubectl", "delete", "pod", pod_name, "-n", namespace], + check=True) + print(f"INFO: Pod {pod_name} deleted successfully.") + except subprocess.CalledProcessError as err: + print(f"WARNING: Failed to delete pod {pod_name}: {err}") + + +def create_workflowtemplate_and_workflow(workflow_template_dict: dict, + workflow_dict: dict): + """ + Function to create workflow and workflow template + Args: + workflow_template_dict(dict): Wokflow templates + workflow_dict(dict): Workflows + Returns: + None + """ + api_instance = client.CustomObjectsApi() + group = "argoproj.io" + version = "v1alpha1" + namespace = "argo" + plural = "workflowtemplates" + + # Create the WorkflowTemplate in Kubernetes + try: + api_response = api_instance.create_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + body=workflow_template_dict, + ) + print("INFO: WorkflowTemplate created.") + except client.exceptions.ApiException as err: + if err.status == 409: + print("INFO: WorkflowTemplate already exists, updating it...") + api_response = api_instance.patch_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + name=workflow_template_dict["metadata"]["name"], + body=workflow_template_dict, + ) + print("INFO: WorkflowTemplate updated.") + else: + print(f"ERROR: Exception when creating WorkflowTemplate: {err}") + sys.exit(1) + + # Submit the workflow + try: + api_response = api_instance.create_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural="workflows", + body=workflow_dict, + ) + print("INFO: Workflow submitted. Status:", api_response) + return api_response + except client.exceptions.ApiException as err: + print(f"ERROR: Exception when submitting workflow: {err}") + sys.exit(1) + return None + + +def main(): + """ + The main entry point + """ + if len(sys.argv) != 4: + print("Usage: python script.py " + " ") + sys.exit(1) + + workflow_template_file = sys.argv[1] + workflow_file = sys.argv[2] + new_version = sys.argv[3] + + config.load_kube_config() + + with open(workflow_template_file, "r", encoding="utf-8") as stream: + workflow_template_str = stream.read() + + # Update the image version in the WorkflowTemplate + if "update_cpc_workflow.yaml" in workflow_file: + old_image_prefix = "cray-product-catalog-update:" + old_version = "2.0.1" + elif "iuf_base_workflow.yaml" in workflow_file: + old_image_prefix = "iuf:" + old_version = "v0.1.12" # Existing version for iuf + else: + print(f"ERROR: Unknown workflow file: {workflow_file}") + sys.exit(1) + + # Update the image version in the WorkflowTemplate + updated_workflow_template_str = update_image_version_in_template( + workflow_template_str, old_image_prefix, old_version, new_version) + + # Load the updated string back into a YAML dict + workflow_template_dict = yaml.safe_load(updated_workflow_template_str) + + # Load the Workflow YAML file + with open(workflow_file, "r", encoding="utf-8") as stream: + workflow_dict = yaml.safe_load(stream) + + api_response = create_workflowtemplate_and_workflow( + workflow_template_dict, workflow_dict) + workflow_name = api_response.get("metadata", {}).get("name") + + if not workflow_name: + print("ERROR: Workflow name not found in the response.") + sys.exit(1) + + workflow_template_name = workflow_template_dict["metadata"]["name"] + + print(f"INFO: Waiting for workflow {workflow_name} to succeed...") + # Wait for the workflow to succeed + time.sleep(15) + workflow_status = wait_for_workflow_to_succeed("argo", workflow_name) + + if workflow_status: + print( + "INFO: Workflow completed successfully, proceeding to next step.") + else: + print("ERROR: Workflow did not succeed, aborting.") + sys.exit(1) + + delete_resources("argo", workflow_name, workflow_template_name) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_apis/__init__.py b/src/csm_testing/tests/iuf_apis/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_apis/__main__.py b/src/csm_testing/tests/iuf_apis/__main__.py new file mode 100644 index 00000000..1c0da127 --- /dev/null +++ b/src/csm_testing/tests/iuf_apis/__main__.py @@ -0,0 +1,122 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script executes iuf apis testing. +""" + +import time +import sys +import urllib3 +from csm_testing.lib.iuf_constants import MEDIA_DIR +from csm_testing.lib.iuf_common import media_dir_setup + +from csm_testing.lib.iuf_classes import ApiInterface +from csm_testing.tests.iuf_apis.apis_list_functions import ( + no_auth_list_stages, + list_activities, + list_stages, +) +from csm_testing.tests.iuf_apis.apis_activity_functions import ( + activity_create, + activity_restart, + activity_resume, + activity_run, + activity_abort, +) +from csm_testing.tests.iuf_apis.apis_session_function import ( + get_activity_session, + get_history, + get_history_time, + get_sessions, + get_workflows, +) + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + +def main(): + """ + The main entry point of the program + """ + tar_dir = "/opt/cray/tests/install/ncn/scripts/iuf_run_setup" + media_dir_setup(tar_dir) + activity = sys.argv[1] + print(f"INFO: This is the activity: {activity}") + + count = 0 + apis = ApiInterface() + count = no_auth_list_stages(count) + print("*" * 50) + + count = list_stages(apis, count) + print("*" * 50) + + count = list_activities(apis, count) + print("*" * 50) + + count = activity_create(apis, activity, count) + print("*" * 50) + + count = activity_run(apis, activity, MEDIA_DIR, count) + print("*" * 50) + time.sleep(3) + + count = activity_abort(apis, activity, count) + print("*" * 50) + time.sleep(3) + + count = activity_resume(apis, activity, count) + print("*" * 50) + time.sleep(3) + + count = activity_abort(apis, activity, count) + print("*" * 50) + time.sleep(3) + + count = activity_restart(apis, activity, count) + print("*" * 50) + + count = get_sessions(apis, activity, count) + print("*" * 50) + + count = get_workflows(apis, activity, count) + print("*" * 50) + + count = get_history(apis, activity, count) + print("*" * 50) + + count = get_history_time(apis, activity, count) + print("*" * 50) + + count = get_activity_session(apis, activity, count) + print("*" * 50) + + print("~" * 50) + print( + f"INFO: Total test cases passed: {count} test cases skipped: {15 - count}" + ) + print("~" * 50) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_apis/apis_activity_functions.py b/src/csm_testing/tests/iuf_apis/apis_activity_functions.py new file mode 100644 index 00000000..feeadcc3 --- /dev/null +++ b/src/csm_testing/tests/iuf_apis/apis_activity_functions.py @@ -0,0 +1,219 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script calls the activity apis and is being imported by __main__.py for iuf_apis +""" +import sys +import copy +from urllib.error import HTTPError +import requests + + +def print_no_of_test_passed(ex, count: int): + """ + Function to print the number of test cases passed + Args: + ex(str): the messages to be printed + Returns: + None + """ + print(f"ERROR: {ex}") + print("~" * 50) + print( + f"INFO: TOTAL test cases passed: {count} test cases skipped: {15 - count}" + ) + print("~" * 50) + sys.exit(1) + + +def activity_create(apis, activity: str, count: int): + """ + Create an activity + Args: + apis: ApiInterface object + activity: Activity name + Returns: + None + """ + print("TEST CASE: Create Activity") + print(f"INFO: Attempting to create activity {activity}") + payload = {"input_parameters": {}, "name": activity} + + if not apis.activity_exists(activity): + try: + print(apis.post_activity(payload)) + print(f"INFO: Created activity: {activity}") + count += 1 + return count + except HTTPError as ex: + print(f"ERROR: Unable to create activity: {activity}") + print_no_of_test_passed(ex, count) + else: + print("WARNING: Skipping the test case: Create Activity") + print(f"INFO: Activity {activity} already exists") + return count + + +def activity_run(apis, activity: str, media_dir: str, count: int): + """Running an activity + + Args: + apis : ApiInterface object + activity (str): Activity name + media_dir (str): Media directory path + """ + print("TEST CASE: Run Activity") + print(f"INFO: Attempting to run activity {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + payload = { + "input_parameters": { + media_dir: media_dir, + "site_parameters": "", + "limit_management_nodes": None, + "limit_managed_nodes": ["Compute"], + "managed_rollout_strategy": "stage", + "concurrent_management_rollout_percentage": 20, + "media_host": "ncn-m001", + "concurrency": 0, + "bootprep_config_managed": "", + "bootprep_config_management": "", + "stages": ["process-media"], + "force": False, + }, + "name": activity, + } + print("TEST CASE: Patch Activity") + try: + print(apis.post_activity_history_run(activity, payload)) + count += 1 + print(f"INFO: Activity {activity} is started") + + # Generate site_parameters and patch the activity. + patched_payload = copy.deepcopy(payload) + # patched_payload["site_parameters"] = self.site_conf.site_params + + # Remove the "force" key from input_parameters for the patched + # activity. + patched_payload["input_parameters"].pop("force", None) + print(f"INFO: Patch activity: {activity}") + print(apis.patch_activity(activity, patched_payload)) + count += 1 + return count + except HTTPError as ex: + print(f"ERROR: Unable to run activity {activity}") + print_no_of_test_passed(ex, count) + + +def activity_abort(apis, activity: str, count: int): + """ + Function to abort an activity + + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Abort Activity") + print(f"INFO: Attempting to abort activity {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + payload = { + "input_parameters": {}, + "name": activity, + "comment": "sending an abort", + "force": None, + } + try: + print(apis.abort_activity(activity, payload)) + print(f"INFO: Aborted activity: {activity}") + count += 1 + return count + except requests.ReadTimeout: + print("ERROR: Timed out sending an abort request.") + msg = f"ERROR: Ensure the argo workflow for {activity} is not running." + print_no_of_test_passed(msg, count) + except HTTPError as ex: + print(f"ERROR: Unable to abort activity: {activity}") + print_no_of_test_passed(ex, count) + + +def activity_resume(apis, activity: str, count: int): + """ + Function to resume an activity + Args: + apis: ApiIntergace abject + activity(str): Activity name + """ + print("TEST CASE: Resume Activity") + print(f"INFO: Attempting to resume activity {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + payload = { + "input_parameters": {}, + "comment": "Restart activity ", + "activity_name": activity, + "force": False, + } + + try: + api_results = apis.post_resume(activity, payload) + print(api_results) + print(f"INFO: Resumed activity: {activity}") + count += 1 + return count + except HTTPError as ex: + print(f"ERROR: Unable to resume activity {activity}") + print_no_of_test_passed(ex, count) + + +def activity_restart(apis, activity: str, count: int): + """ + Function to restart an activity + Args: + apis: ApiInterface abject + activity(str): Activity name + """ + print("TEST CASE: Restart Activity") + print(f"INFO: Attempting to restart activity {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + payload = { + "input_parameters": {}, + "comment": "Restart activity ", + "activity_name": activity, + "force": False, + } + + try: + api_results = apis.post_restart(activity, payload) + print(api_results) + print(f"INFO: Restarted activity: {activity}") + count += 1 + return count + except HTTPError as ex: + print(f"ERROR: Unable to restart activity: {activity}") + print_no_of_test_passed(ex, count) diff --git a/src/csm_testing/tests/iuf_apis/apis_list_functions.py b/src/csm_testing/tests/iuf_apis/apis_list_functions.py new file mode 100644 index 00000000..56514b52 --- /dev/null +++ b/src/csm_testing/tests/iuf_apis/apis_list_functions.py @@ -0,0 +1,93 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script calls the list apis and is being imported by __main__.py for iuf_apis +""" + +from urllib.error import HTTPError +import requests +from csm_testing.tests.iuf_apis.apis_activity_functions import ( + print_no_of_test_passed, ) + + +def no_auth_list_stages(count: int): + """ + Function to try and access apis without a token + Args: + None + Returns: + None + """ + print("TEST CASE: w/o security API call: Try Api Call without token") + try: + api_response = requests.get( + "https://api-gw-service-nmn.local/apis/iuf/v1/stages") + except HTTPError as err: + print_no_of_test_passed(err, count) + print("INFO: Api Call without token has passed") + count += 1 + return count + + +def list_stages(apis, count: int): + """ + Function to list iuf stages + Args: + apis: ApiInterface Object + """ + print("TEST CASE: List Stages") + try: + stage_result = apis.get_stages() + print(stage_result) + stages = stage_result.json() + except HTTPError as ex: + print_no_of_test_passed(ex, count) + + if stages is not None: + stage_list = [stage["name"] for stage in stages["stages"]] + print("\n".join(stage_list)) + count += 1 + return count + msg = "" + print_no_of_test_passed(msg, count) + + +def list_activities(apis, count: int): + """ + Function to list iuf activities + Args: + apis: ApiInterface object + """ + print("TEST CASE: List Activities") + try: + activities_result = apis.get_activities() + print(activities_result) + count += 1 + activities = activities_result.json() + except HTTPError as ex: + print_no_of_test_passed(ex, count) + + if activities is not None: + act_list = sorted([act["name"] for act in activities]) + print("\n".join(act_list)) + return count diff --git a/src/csm_testing/tests/iuf_apis/apis_session_function.py b/src/csm_testing/tests/iuf_apis/apis_session_function.py new file mode 100644 index 00000000..9a4be7db --- /dev/null +++ b/src/csm_testing/tests/iuf_apis/apis_session_function.py @@ -0,0 +1,194 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script executes session apis testing. +""" + +from urllib.error import HTTPError +from csm_testing.tests.iuf_apis.apis_activity_functions import ( + print_no_of_test_passed, ) + + +def get_sessions(apis, activity: str, count: int): + """ + Function to get activity sessions + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Get Sessions") + print(f"INFO: Get Sessions for activity: {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + + try: + sessions_result = apis.get_activity_sessions(activity) + print(sessions_result) + print(f"INFO: Sessions for activity: {activity}") + sessions = sessions_result.json() + except HTTPError as ex: + print(f"ERROR: Unable to get sessions for activity: {activity}") + print_no_of_test_passed(ex, count) + + if sessions is not None: + session_list = [session["name"] for session in sessions] + print("\n".join(session_list)) + count += 1 + return count + msg = f"ERROR: Sessions not found for activity: {activity}" + print_no_of_test_passed(msg, count) + + +def get_workflows(apis, activity: str, count: int): + """ + Function to get activity workflows + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Get Workflows") + print(f"INFO: Get Workflows for activity: {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + + try: + sessions_result = apis.get_activity_sessions(activity) + print(sessions_result) + print(f"INFO: Workflows for activity: {activity}") + sessions = sessions_result.json() + except HTTPError as ex: + print(f"ERROR: Unable to get workflows for activity: {activity}") + print_no_of_test_passed(ex, count) + + if sessions is not None: + session_workflows = [session["workflows"] for session in sessions] + workflow_list = [] + for session_workflow in session_workflows: + for workflow in session_workflow: + workflow_list.append(workflow["id"]) + print("\n".join(workflow_list)) + count += 1 + return count + msg = f"ERROR: workflows not found for activity: {activity}" + print(msg) + + +def get_history(apis, activity: str, count: int): + """ + Function to get activity history + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Get History") + print(f"INFO: Get history for activity: {activity}") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + try: + history = apis.get_activity_history(activity) + except HTTPError as ex: + print(f"ERROR: Unable to get history for activity: {activity}") + print_no_of_test_passed(ex, count) + + if history is not None: + print(history) + count += 1 + return count + msg = "ERROR: History not found for activity: {activity}" + print_no_of_test_passed(msg, count) + + +def get_history_time(apis, activity: str, count: int): + """ + Function to get activity history time + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Get History/time") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist." + print_no_of_test_passed(msg, count) + try: + history = apis.get_activity_history(activity).json() + except HTTPError as ex: + print(f"ERROR: Unable to get history for activity: {activity}") + print_no_of_test_passed(ex, count) + if history is not None: + time = history[0]["start_time"] + else: + msg = "ERROR: History not found" + print_no_of_test_passed(msg, count) + + print(f"INFO: Get history/time for activity: {activity} , time:{time}") + try: + history_time = apis.get_activity_history_time(activity, time) + except HTTPError as ex: + print( + f"ERROR: Unable to get history/time for activity: {activity} , time:{time}" + ) + print_no_of_test_passed(ex, count) + + if history_time is not None: + print(history_time) + count += 1 + return count + msg = "ERROR: history/time for activity: {activity} , time:{time} not found" + print_no_of_test_passed(msg, count) + + +def get_activity_session(apis, activity: str, count: int): + """ + Function to get activity sessions + Args: + apis: ApiInterface object + activity(str): Activity name + """ + print("TEST CASE: Get Activity Session") + if not apis.activity_exists(activity): + msg = f"ERROR: Activity {activity} does not exist. or Backend is not working properly" + print_no_of_test_passed(msg, count) + sessions = apis.get_activity_sessions(activity).json() + if sessions is not None: + session = sessions[0]["name"] + else: + msg = f"ERROR: Sessions not found for activity: {activity}" + print_no_of_test_passed(msg, count) + + print( + f"INFO: Get activity/session for activity: {activity} , session:{session}" + ) + + try: + session = apis.get_activity_session(activity, session) + print(session) + count += 1 + return count + except HTTPError as ex: + print( + f"ERROR: Unable to get activity/session for activity: {activity} , session:{session}" + ) + print_no_of_test_passed(ex, count) diff --git a/src/csm_testing/tests/iuf_cleanup/__init__.py b/src/csm_testing/tests/iuf_cleanup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_cleanup/__main__.py b/src/csm_testing/tests/iuf_cleanup/__main__.py new file mode 100644 index 00000000..8a1beaad --- /dev/null +++ b/src/csm_testing/tests/iuf_cleanup/__main__.py @@ -0,0 +1,212 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script executes deletion of logs , workflows and directories created for an iuf activity. +""" + +import subprocess +import sys +from pathlib import Path +from csm_testing.lib.iuf_constants import MEDIA_DIR + +LOG_DIR = "/etc/cray/upgrade/csm/iuf" + + +def delete_workflows(activity_name: str): + """ + Function to get the list of workflows for the activity. + Args: + activity_name(str): The activity name whose workflows list needs to be generated + """ + command = ("kubectl get workflow -n argo -o " + f"custom-columns=NAME:.metadata.name|grep {activity_name}") + + workflows = [] + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + if not result.returncode: + workflows = result.stdout.splitlines() + except subprocess.CalledProcessError as err: + print(err) + + if isinstance(workflows, list) and all( + isinstance(wf, str) for wf in workflows): + print(f"INFO: Workflows found for {activity_name} :{workflows}") + for workflow in workflows: + command_delete_workflow = f"kubectl delete workflow {workflow} -n argo" + try: + result = subprocess.run( + command_delete_workflow, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print("INFO: Command output:", result.stdout) + except subprocess.CalledProcessError as err: + print(f"ERROR: Command failed with an error: {err}") + except Exception as err: + print(f"ERROR: Unable to delete workflow {workflow} , {err}") + else: + print(f"WARNING: Workflows not found for {activity_name}") + + +def delete_configmaps(activity_name: str): + """ + Function to get the list of configmaps for the activity. + Args: + activity_name(str): The activity name whose configmaps list needs to be generated + + """ + command = ("kubectl get configmap -n argo -o " + f"custom-columns=NAME:.metadata.name|grep {activity_name}") + + configmaps = [] + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + if not result.returncode: + configmaps = result.stdout.splitlines() + except subprocess.CalledProcessError as err: + print(err) + + if isinstance(configmaps, list) and all( + isinstance(cm, str) for cm in configmaps): + print(f"INFO: configmaps found for {activity_name} :{configmaps}") + for configmap in configmaps: + command_delete_configmap = f"kubectl delete configmap {configmap} -n argo" + try: + result = subprocess.run( + command_delete_configmap, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print("INFO: Command output:", result.stdout) + except subprocess.CalledProcessError as err: + print(f"ERROR: Command failed with an error: {err}") + except Exception as err: + print(f"ERROR: Unable to delete configmap {configmap} , {err}") + else: + print(f"WARNING: Workflows not found for {activity_name}") + + +def cleanup(activity_name="test-activity"): + """ + Function to remove the log files, media directories, workflows and configmaps for the activity. + Args: + activity_name(str, optional): The activity name whose logs, media dir(s), + workflows and configmaps need to be deleted. + Defaults to "test-activity". + Returns: + None + + """ + command_delete_logs = f"rm -r {LOG_DIR}/{activity_name}" + command_delete_media_dir = f"rm -r {MEDIA_DIR}" + # Deleting log files for the activity + logs_path = Path(f"{LOG_DIR}/{activity_name}") + if logs_path.is_dir(): + print( + f"INFO: {logs_path} exists. Deleting log directory for activity: {activity_name}" + ) + try: + result = subprocess.run( + command_delete_logs, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + except subprocess.CalledProcessError as err: + print(f"ERROR: {err}") + else: + print(f"WARNING: {logs_path} does not exist.") + + # Deleting media dir for the activity + media_path = Path(MEDIA_DIR) + if media_path.is_dir(): + print( + f"INFO: {media_path} exists. Deleting media directory for activity: {activity_name}" + ) + try: + result = subprocess.run( + command_delete_media_dir, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + except subprocess.CalledProcessError as err: + print(f"ERROR: {err}") + else: + print(f"WARNING: {media_path} does not exist.") + + # Deleting workflows for the activity + delete_workflows(activity_name) + + # Deleting configmaps for the activity + delete_configmaps(activity_name) + + +def main(): + """ + The main entry point of the program. + Args: + None + Returns: + None + """ + print() + print("INFO: Running cleanup..") + if len(sys.argv) > 2: + print("Usage: script.py ") + sys.exit(1) + elif len(sys.argv) == 2: + activity_name = sys.argv[1] + cleanup(activity_name) + else: + cleanup() + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_post_checks/__init__.py b/src/csm_testing/tests/iuf_post_checks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_post_checks/__main__.py b/src/csm_testing/tests/iuf_post_checks/__main__.py new file mode 100644 index 00000000..e6073ef6 --- /dev/null +++ b/src/csm_testing/tests/iuf_post_checks/__main__.py @@ -0,0 +1,155 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script performs post-checks for IUF run by checking if + 1. log directory was created + 2. activity specific configmap was created + 3. state directory was created and relevant files are present inside or not. +""" + +import os +import subprocess +import sys +from csm_testing.lib.iuf_constants import MEDIA_DIR + + +def check_logs(activity: str): + """ + Function to check the logs for the activity and print the info. + Args: + activity(str): Activity name + Returns: + None + """ + # Define the log directory path + log_dir = f"/etc/cray/upgrade/csm/iuf/{activity}/log" + + # Check if the log directory exists + if os.path.exists(log_dir): + print(f"TEST CASE: Log directory exists for activity: {activity}") + else: + print(f"ERROR: Log directory does NOT exist for activity: {activity}") + sys.exit(1) + + +def check_configmap(activity: str): + """ + Function to check the configmap for the activity and print the info. + Args: + activity(str): Activity name + Returns: + None + """ + configmap_name = activity + namespace = "argo" + + try: + # Use kubectl command to check if the configmap exists in the Argo namespace + subprocess.run( + ["kubectl", "get", "configmap", configmap_name, "-n", namespace], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print( + f"TEST CASE: ConfigMap '{configmap_name}' exists in the '{namespace}' namespace." + ) + + except subprocess.CalledProcessError as err: + print( + f"ERROR: ConfigMap '{configmap_name}' does NOT exist in the '{namespace}' namespace.\n" + f"Error: {err}") + sys.exit(1) + + +def check_state(activity: str): + """ + Function to check the contents of the state directory. + Args: + activity(str): Activity name + Returns: + None + """ + state_dir = f"/etc/cray/upgrade/csm/iuf/{activity}/state" + + print("INFO: Checking for state folder contents") + if os.path.exists(state_dir): + print(f"TEST CASE: State directory exists for activity: {activity}") + if os.path.exists(f"{state_dir}/activity_dict.yaml"): + print( + f"TEST CASE: activity_dict.yaml present for {activity} in state folder" + ) + else: + print( + f"ERROR: activity_dict.yaml not present for {activity} in state folder" + ) + sys.exit(1) + + if os.path.exists(f"{state_dir}/stage_hist.yaml"): + print( + f"TEST CASE: stage_hist.yaml present for {activity} in state folder" + ) + else: + print( + f"ERROR: stage_hist.yaml not present for {activity} in state folder" + ) + sys.exit(1) + else: + print( + f"ERROR: State directory does NOT exist for activity: {activity}") + sys.exit(1) + + print("INFO:Checking session_vars") + if os.path.exists(f"{MEDIA_DIR}/session_vars.yaml"): + print(f"TEST CASE: session_vars present for {activity} ") + else: + print(f"ERROR: session_vars not present for {activity} ") + sys.exit(1) + + +def main(): + """ + The main entry point of the program. + Args: + None + Returns: + None + """ + print() + print("INFO: Running IUF post-checks...") + if len(sys.argv) > 2: + print("Usage: iuf_post_checks.py ") + sys.exit(1) + activity_name = sys.argv[1] + check_state(activity_name) + check_logs(activity_name) + check_configmap(activity_name) + print( + "------------------------ END OF POST-CHECKS ------------------------") + print() + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_pre_checks/__init__.py b/src/csm_testing/tests/iuf_pre_checks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_pre_checks/__main__.py b/src/csm_testing/tests/iuf_pre_checks/__main__.py new file mode 100644 index 00000000..2405af38 --- /dev/null +++ b/src/csm_testing/tests/iuf_pre_checks/__main__.py @@ -0,0 +1,554 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script runs pre-checks for IUF. +""" + +import os +import sys +import shutil +import subprocess +import requests +from packaging import version +from csm_testing.lib.iuf_common import run_command + + +def compare_versions(version1, version2): + """ + Function to compare given two versions and print the result info. + Args: + version1(int/float), version2(int/float) : versions to compare + Returns: + None + """ + ver_1 = version.parse(version1) + ver_2 = version.parse(version2) + + if ver_1 == ver_2: + print( + f"INFO: Version {version1} is equal to required version {version2}" + ) + elif ver_1 < ver_2: + print( + f"ERROR: Version {version1} is less than required version {version2}" + ) + sys.exit(1) + else: + print( + f"INFO: Version {version1} is greater than required version {version2}" + ) + + +def check_proxy(): + """ + Function to check proxy settings in the session and print the info. + Args: + None + Returns: + None + """ + + http_proxy = os.environ.get("http_proxy") + https_proxy = os.environ.get("https_proxy") + no_proxy = os.environ.get("no_proxy") + + proxy_variables = { + "http_proxy": http_proxy, + "https_proxy": https_proxy, + "no_proxy": no_proxy, + } + + set_proxies = { + key: value + for key, value in proxy_variables.items() if value + } + + if set_proxies: + for key, value in set_proxies.items(): + print(f"{key} is set to: {value}") + + print("ERROR: Proxy variables should not be set.") + sys.exit(1) + else: + print("INFO: No proxy variables are set.") + + +def check_k8s_version(minimum_version): + """ + Function to check k8s version and print the info. + Args: + minimum_version(int/float): Kubernetes version + Returns: + None + """ + k8s_version, returncode = run_command( + "kubectl version --short | grep -i 'server version' | awk '{print $3}'" + ) + if returncode != 0: + print( + f"ERROR: Failed to get Kubernetes version. Return code: {returncode}" + ) + sys.exit(1) + + print(f"INFO: Kubernetes version: {k8s_version}") + compare_versions(k8s_version, minimum_version) + + +def check_iuf_cli_version(minimum_version): + """ + Function to check iuf cli version and print the info. + Args: + minimum_version(int/float): IUF CLI version + Returns: + None + """ + iuf_cli_version, returncode = run_command( + "rpm -qa | grep iuf-cli | awk -F- '{print $3}'") + if returncode != 0: + print( + "ERROR: Failed to get iuf-cli version. Return code: {returncode}") + sys.exit(1) + + if not iuf_cli_version: + print("ERROR: iuf-cli rpm is not installed") + sys.exit(1) + else: + print(f"INFO: iuf-cli rpm is installed: {iuf_cli_version}") + + compare_versions(iuf_cli_version, minimum_version) + + +def check_nls_image(minimum_version): + """ + Function to check cray-nls image and print the info. + Args: + minimum_version(int/float): NLS Image version + Returns: + None + """ + cray_nls_version, returncode = run_command( + "kubectl get deployment cray-nls -n argo \ +-o=jsonpath='{.spec.template.spec.containers[*].image}'") + cray_nls_version = cray_nls_version.split(":")[1] + if returncode != 0: + print( + f"ERROR: Failed to get cray-nls version. Return code: {returncode}" + ) + sys.exit(1) + + print(f"INFO: cray-nls version: {cray_nls_version}") + compare_versions(cray_nls_version, minimum_version) + + +def check_nls_pod_status(): + """ + Function to check cray-nls pod status and print the info. + Args: + None + Returns: + None + """ + nls_pods, returncode = run_command( + "kubectl get deployment cray-nls -n argo -o=jsonpath='{.status.availableReplicas}'" + ) + + if returncode == 0 and int(nls_pods) == 3: + print("INFO: All cray-nls pods are running") + elif returncode == 0: + print("ERROR: Some cray-nls pods are not running") + print(nls_pods) + sys.exit(1) + else: + print(f"Unexpected error: Return code {returncode}") + print(nls_pods) + sys.exit(1) + + +def check_nls_workflow_server(): + """ + Function to check cray-nls workflow server and print the info. + Args: + None + Returns: + None + """ + nls_workflow_server, returncode = run_command( + "kubectl get deployment cray-nls-argo-workflows-server \ +-n argo -o=jsonpath='{.status.availableReplicas}'") + + if returncode == 0 and int(nls_workflow_server) == 3: + print("INFO: All cray-nls-argo-workflows-server pods are running") + elif returncode == 0: + print( + "ERROR: Some cray-nls-argo-workflows-server pods are not running") + print(nls_workflow_server) + sys.exit(1) + else: + print(f"Unexpected error: Return code {returncode}") + print(nls_workflow_server) + sys.exit(1) + + +def check_nls_workflow_controller(): + """ + Function to check cray-nls controller and print the info. + Args: + None + Returns: + None + """ + nls_controller_pods, returncode = run_command( + "kubectl get deployment cray-nls-argo-workflows-workflow-controller \ +-n argo -o=jsonpath='{.status.availableReplicas}'") + + if returncode == 0 and int(nls_controller_pods) == 2: + print( + "INFO: All cray-nls-argo-workflows-workflow-controller pods are running" + ) + elif returncode == 0: + print( + "ERROR: Some cray-nls-argo-workflows-workflow-controller pods are not running" + ) + print(nls_controller_pods) + sys.exit(1) + else: + print(f"Unexpected error: Return code {returncode}") + print(nls_controller_pods) + sys.exit(1) + + +def check_nls_postgres(): + """ + Function to check cray-nls-postgres statefulset and print the info. + Args: + None + Returns: + None + """ + nls_postgres, returncode = run_command( + "kubectl get statefulset cray-nls-postgres \ +-n argo -o=jsonpath='{.status.availableReplicas}'") + + if returncode == 0 and int(nls_postgres) == 3: + print("INFO: All cray-nls pods are running") + elif returncode == 0: + print("ERROR: Some cray-nls pods are not running") + print(nls_postgres) + sys.exit(1) + else: + print(f"Unexpected error: Return code {returncode}") + print(nls_postgres) + sys.exit(1) + + +def check_cray_iuf_chart(minimum_version): + """ + Function to check cray-iuf helm chart and print the info. + Args: + minimum_version(int/float): Cray IUF Chart Version + Returns: + None + """ + cray_iuf_chart_version, returncode = run_command( + "helm get values cray-iuf -n argo -o json | jq -r '.global.chart.version'" + ) + if returncode != 0: + print( + "ERROR: Failed to get cray iuf chart version. Return code: {returncode}" + ) + sys.exit(1) + + if not cray_iuf_chart_version: + print("ERROR: cray iuf chart is not installed") + sys.exit(1) + else: + print(f"INFO: cray iuf chart is installed: {cray_iuf_chart_version}") + + compare_versions(cray_iuf_chart_version, minimum_version) + + +def check_cray_nls_chart(minimum_version): + """ + Function to check cray-nls helm chart and print the info. + Args: + minimum_version(int/float): Cray NLS Chart Version + Returns: + None + """ + cray_nls_chart_version, returncode = run_command( + "helm get values cray-nls -n argo -o json | jq -r '.global.chart.version'" + ) + if returncode != 0: + print( + "ERROR: Failed to get cray nls chart version. Return code: {returncode}" + ) + sys.exit(1) + + if not cray_nls_chart_version: + print("ERROR: cray nls chart is not installed") + sys.exit(1) + else: + print(f"INFO: cray nls chart is installed: {cray_nls_chart_version}") + + compare_versions(cray_nls_chart_version, minimum_version) + + +def check_cfs(): + """ + Function to check cfs pods and print the info. + Args: + None + Returns: + None + """ + command = "kubectl get po -n services | grep -i '^cray-cfs-api' | grep -v 'Running'" + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + cfs_api_pods = result.stdout.decode().strip() + if result.returncode == 0 and cfs_api_pods: + print("ERROR: Some cray-cfs-api pods are not running") + print(cfs_api_pods) + sys.exit(1) + + else: + print(f"Unexpected error: Return code {result.returncode}") + print(cfs_api_pods) + sys.exit(1) + except subprocess.CalledProcessError: + print("INFO: All cray-cfs-api* pods are running") + except Exception: + sys.exit(1) + + command = ( + "kubectl get po -n services | grep -i '^cfs-ara-postgres' | grep -v 'Running'" + ) + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + cfs_ara_pods = result.stdout.decode().strip() + if result.returncode == 0 and cfs_ara_pods: + print("ERROR: Some pods are not running") + print(cfs_ara_pods) + sys.exit(1) + else: + print(f"ERROR: Return code {result.returncode}") + print(cfs_ara_pods) + sys.exit(1) + except subprocess.CalledProcessError: + print("INFO: All cfs-ara-postgres* pods are running") + except Exception: + sys.exit(1) + + +def check_sat(): + """ + Function to check sat status and print the info. + Args: + None + Returns: + None + """ + sat, returncode = run_command("sat --version") + if returncode == 0: + print(f"INFO: sat is installed. {sat}") + else: + print( + "ERROR: Unable to run sat.Please verify if sat is installed and working" + ) + sys.exit(1) + + +def check_docs_and_libcsm(): + """ + Function to check docs-csm and lib-csm rpms and print the info. + Args: + None + Returns: + None + """ + docs_csm, returncode = run_command("rpm -qa | grep docs-csm") + if returncode == 0: + print(f"INFO: docs-csm rpm is installed. Version: {docs_csm}") + else: + print( + "ERROR: Unable to find docs. Please verify if docs is installed.") + sys.exit(1) + lib_csm, returncode = run_command("rpm -qa | grep docs-csm") + if returncode == 0: + print(f"INFO: lib-csm is installed. Version: {lib_csm}") + else: + print( + "ERROR: Unable to find libcsm. Please verify if libcsm is installed." + ) + sys.exit(1) + + +def check_cpc(): + """ + Function to check cray-product-catalog configmap and print the info. + Args: + None + Returns: + None + """ + _, returncode = run_command( + "kubectl get cm -n services | grep cray-product-catalog") + if returncode == 0: + print( + "INFO: Cray-Product-Catalog configmap exists in services namespace" + ) + else: + print("ERROR: Unable to find Cray-Product-Catalog configmap.") + sys.exit(1) + + +def check_available_space(): + """ + Function to check available disk space and print the info. + Args: + None + Returns: + None + """ + _, _, free = shutil.disk_usage("/etc/cray/upgrade/csm/") + free_gb = free // (2**30) + if free_gb < 5: + print("ERROR: Insufficient space in /etc/cray/upgrade/csm/.") + sys.exit(1) + else: + print(f"INFO: Available space: {free_gb}G") + + +def check_url_status(cluster_name: str): + """ + Function to check argo,vcs,nexus reachability and print the info. + Args: + cluster_name(str): Cluster name + Returns: + None + """ + urls = [ + f"https://argo.cmn.{cluster_name}.hpc.amslabs.hpecorp.net/", + f"https://vcs.cmn.{cluster_name}.hpc.amslabs.hpecorp.net/", + f"https://nexus.cmn.{cluster_name}.hpc.amslabs.hpecorp.net/", + ] + + for url in urls: + try: + response = requests.head(url, allow_redirects=True, timeout=5) + if response.status_code not in [200, 302]: + print( + f"Error: {url} Unreachable. Received status code {response.status_code}" + ) + sys.exit(1) + else: + print(f"INFO: {url} is reachable.") + except requests.RequestException as err: + print(f"ERROR: {url} Unreachable. Exception: {err}") + sys.exit(1) + + +def check_ssh(): + """ + Function to check ssh connectivity and print the info. + Args: + None + Returns: + None + """ + ssh_command = "ssh -o BatchMode=yes -q ncn-m002 exit" + ssh_output, returncode = run_command(ssh_command) + + if returncode == 0: + print( + "INFO: SSH to ncn-m002 successful. Now going back to ncn-m001...") + ssh_command_2 = "ssh -o BatchMode=yes -q ncn-m001 exit" + ssh_output_2, r_code = run_command(ssh_command_2) + if r_code == 0: + print("INFO: SSH to ncn-m001 successful.") + else: + print("ERROR: Unable to SSH to ncn-m001. Error:", ssh_output_2) + else: + print("ERROR: Unable to SSH to ncn-m002. Error:", ssh_output) + + +def main(): + """ + The main entry point of the program + Args: + None + Returns: + None + """ + print("INFO: Running IUF pre-checks") + if len(sys.argv) != 7: + print("Usage: script.py \ + \ + \ +") + sys.exit(1) + + k8s_minimum_version = sys.argv[1] + iuf_cli_minimum_version = sys.argv[2] + cluster_name = sys.argv[3] + nls_minimum_version = sys.argv[4] + iuf_chart_minimum_version = sys.argv[5] + nls_chart_minimum_version = sys.argv[6] + + check_proxy() + check_k8s_version(k8s_minimum_version) + check_iuf_cli_version(iuf_cli_minimum_version) + check_nls_image(nls_minimum_version) + check_nls_pod_status() + check_nls_workflow_server() + check_nls_workflow_controller() + check_nls_postgres() + check_cray_iuf_chart(iuf_chart_minimum_version) + check_cray_nls_chart(nls_chart_minimum_version) + check_cfs() + check_sat() + check_docs_and_libcsm() + check_cpc() + check_available_space() + check_url_status(cluster_name) + check_ssh() + + print("-----------------------END OF PRE-CHECKS--------------------") + print() + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_run/__init__.py b/src/csm_testing/tests/iuf_run/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_run/__main__.py b/src/csm_testing/tests/iuf_run/__main__.py new file mode 100644 index 00000000..c34b31f0 --- /dev/null +++ b/src/csm_testing/tests/iuf_run/__main__.py @@ -0,0 +1,90 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script sets up the media directory and executes process-media stage in IUF +for creating an activity. +""" + +import subprocess +import sys +from csm_testing.lib.iuf_constants import MEDIA_DIR +from csm_testing.lib.iuf_common import media_dir_setup + + +def run(*args): + """ + Runs process media using dummy product + + Args: tar_dir is from where media_dir content will be copied, + for the IUF run + Returns: None + """ + tar_dir = args[0] + activity_name = args[1] + if len(args) == 3: + log_dir = args[2] + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} --log-dir {log_dir} run " + f"-rv {MEDIA_DIR}/product_vars.yaml -r process-media") + else: + command = (f"iuf -a {activity_name} -m {MEDIA_DIR} run " + f"-rv {MEDIA_DIR}/product_vars.yaml -r process-media") + media_dir_setup(tar_dir) + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print("INFO: Command output:", result.stdout) + print("INFO: IUF run completed") + except subprocess.CalledProcessError as err: + print(f"ERROR: {err}") + sys.exit(1) + + +def main(): + """ + The main entry point of the program + Args: None + returns: None + """ + if len(sys.argv) < 3 or len(sys.argv) > 4: + print("Usage: script.py ") + sys.exit(1) + else: + tar_dir = sys.argv[1] + activity_name = sys.argv[2] + if len(sys.argv) == 3: + run(tar_dir, activity_name) + else: + log_dir = sys.argv[3] + run(tar_dir, activity_name, log_dir) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_stages/__init__.py b/src/csm_testing/tests/iuf_stages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_stages/__main__.py b/src/csm_testing/tests/iuf_stages/__main__.py new file mode 100644 index 00000000..234f2e27 --- /dev/null +++ b/src/csm_testing/tests/iuf_stages/__main__.py @@ -0,0 +1,447 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script runs IUF from process-media to prepare-images. +""" + +import subprocess +import os +import sys +import yaml +import requests +from cray_product_catalog.query import ProductCatalog +from csm_testing.lib.iuf_common import ( + run_command, + vcs_auth, +) +from csm_testing.lib.iuf_constants import ( + MEDIA_DIR, + CONFIGMAP_NAME, + NAMESPACE, + PRODUCT_NAME, + PRODUCT_VERSION, + NEXUS_URL, +) +from csm_testing.tests.iuf_stages.helper import ( + nexus_repo_setup, + check_product_data, + run_iuf_script, + validate_images, +) + +REPO_URL = f"{NEXUS_URL}/service/rest/v1/repositories/yum/hosted" + + +def process_media(*args): + """Runs process-media for a dummy-product""" + tar_dir = args[0] + activity_name = args[1] + test_cases = args[2] + + if run_iuf_script(tar_dir, activity_name): + folder_to_check = "/etc/cray/upgrade/csm/automation-tests/dummy-1.0.0" + file_to_check = "/etc/cray/upgrade/csm/automation-tests/session_vars.yaml" + + if os.path.isdir(folder_to_check): + print(f"TEST CASE: Folder exists: {folder_to_check}") + test_cases += 1 + else: + print(f"ERROR: Folder does not exist: {folder_to_check}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + if os.path.isfile(file_to_check): + print(f"TEST CASE: File exists: {file_to_check}") + test_cases += 1 + else: + print(f"ERROR: File does not exist: {file_to_check}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + check_configmap_command = f"kubectl get configmap {activity_name} -n argo" + configmap_output, _ = run_command(check_configmap_command) + + if configmap_output: + print("TEST CASE: ConfigMap found.") + test_cases += 1 + return test_cases + print("ERROR: ConfigMap not found.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + print("ERROR: Unable to run process-media.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def pre_install_check(activity_name: str, test_cases: int): + """Runs pre-install-check for a dummy-product + + Args: + activity_name (str): name of the activity + test_cases (int): number of test cases + + Returns: + int: number of executed test cases + """ + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} run -rv {MEDIA_DIR}/product_vars.yaml " + f"-r pre-install-check") + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print("Command output:", result.stdout) + print( + "TEST CASES: pre-hook, post-hook and on-exit scripts executed successfully" + ) + test_cases += 3 + return test_cases + except subprocess.CalledProcessError as err: + print(f"ERROR: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def deliver_product(activity_name: str, test_cases: int): + """Runs deliver-product for a dummy-product + + Args: + activity_name (str): name of the activity + test_cases (int): number of test cases + + Returns: + int: number of executed test cases + """ + nexus_repo_setup() + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} run -rv {MEDIA_DIR}/product_vars.yaml " + f"-r deliver-product") + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + print("Command output:", result.stdout) + cray_product_catalog = ProductCatalog(name=CONFIGMAP_NAME, + namespace=NAMESPACE) + dummy_product = cray_product_catalog.get_product( + PRODUCT_NAME, PRODUCT_VERSION) + + if not check_product_data(dummy_product): + print("ERROR: Exiting due to missing product data.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + test_cases += 5 + print("INFO: Checking cm...") + check_configmap_command = ( + f"kubectl get configmap {CONFIGMAP_NAME} -n {NAMESPACE} -o json") + configmap_output, _ = run_command(check_configmap_command) + if configmap_output: + print( + "INFO: ConfigMap found. Checking for entry 'dummy' in the product name..." + ) + + # Parse the YAML output to look for the product name "dummy" + configmap_data = yaml.safe_load(configmap_output) + product_data = configmap_data.get("data", {}) + + # Check if "dummy" is in the product data keys + if "dummy" in product_data: + print("TEST CASE: Entry 'dummy' found in the ConfigMap.") + test_cases += 1 + return test_cases + print("ERROR: Entry 'dummy' not found in the ConfigMap.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + print(f"ERROR: ConfigMap {CONFIGMAP_NAME} not found.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + except subprocess.CalledProcessError as err: + print(f"Error: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def update_vcs_config(activity_name: str, test_cases: int): + """Runs update-vcs-config for a dummy-product + + Args: + activity_name (str): name of the activity + test_cases (int): number of test cases + + Returns: + int: number of executed test cases + """ + try: + cray_product_catalog = ProductCatalog(name=CONFIGMAP_NAME, + namespace=NAMESPACE) + dummy_product = cray_product_catalog.get_product("dummy", "1.0.0") + if not dummy_product.configuration: + print("Error: No configurations present!") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + test_cases += 1 + print("TEST CASE: configurations present. Passed!") + + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} run -rv {MEDIA_DIR}/product_vars.yaml " + f"-bm {MEDIA_DIR}/management-bootprep.yaml -r update-vcs-config") + run_command(command) + + dummy_repo_url, auth, ca_cert_path = vcs_auth() + + session = requests.Session() + + print(f"INFO: Attempting to fetch gitea repository:{dummy_repo_url}") + resp = session.get(dummy_repo_url, verify=ca_cert_path, auth=auth) + + if resp.status_code == 200: + print(f"TEST CASE: Repository '{dummy_repo_url}' exists.") + test_cases += 1 + return test_cases + if resp.status_code == 404: + print(f"ERROR: Repository '{dummy_repo_url}' not found.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + print( + f"ERROR: Failed to fetch repository: {resp.status_code}, {resp.text}" + ) + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + repo_info = resp.json() + if repo_info: + print(f"INFO: Repository Name: {repo_info['name']}") + print(f"INFO: Default Branch: {repo_info['default_branch']}") + except subprocess.CalledProcessError as err: + print(f"Error: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def update_cfs_config(activity_name: str, test_cases: int): + """Runs update-cfs-config for a dummy-product + + Args: + activity_name (str): name of the activity + test_cases (int): number of test cases + + Returns: + int: number of executed test cases + """ + try: + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} run -rv {MEDIA_DIR}/product_vars.yaml " + f"-bm {MEDIA_DIR}/management-bootprep.yaml -r update-cfs-config") + run_command(command) + + kubectl_command = ( + f"kubectl get configmaps -n argo {activity_name} -o jsonpath=" + "'{.data.iuf_activity}' | jq '.operation_outputs.stage_params" + '["update-cfs-config"]["update-management-cfs-config"]' + '["sat-bootprep-run"].script_stdout' + "' | xargs -0 echo -e") + + kubectl_output, _ = run_command(kubectl_command) + + if not kubectl_output: + print( + f"ERROR: The output for cfs configurations is null or empty in the " + f"{activity_name} configmap.") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + print("TEST CASE: Configmap updated with cfs configurations data") + test_cases += 1 + + cfs_command = "cray cfs configurations list | grep 'config-minimal-management-dummy-1.0.0'" + cfs_output, _ = run_command(cfs_command) + if not cfs_output: + print("ERROR: No configuration found in cfs") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + print(f"TEST CASE: CFS configuration found {cfs_output}") + test_cases += 1 + return test_cases + except subprocess.CalledProcessError as err: + print(f"Error: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def prepare_images(activity_name: str, test_cases: int): + """Runs prepare-images for a dummy-product + + Args: + activity_name (str): name of the activity + test_cases (int): number of test cases + + Returns: + int: number of executed test cases + """ + try: + command = ( + f"iuf -a {activity_name} -m {MEDIA_DIR} run -rv {MEDIA_DIR}/product_vars.yaml " + f"-bm {MEDIA_DIR}/management-bootprep.yaml -r prepare-images") + run_command(command) + + kubectl_command = ( + f"kubectl get configmaps -n argo {activity_name} -o jsonpath=" + "'{.data.iuf_activity}' | jq '.operation_outputs.stage_params" + '["prepare-images"]["prepare-management-images"]' + '["sat-bootprep-run"].script_stdout' + "' | xargs -0 echo -e") + kubectl_output, _ = run_command(kubectl_command) + + if not kubectl_output: + print( + "Error: The output for configmap entry of images is null or empty." + ) + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + else: + print("INFO: Found the cm entry...") + print(kubectl_output) + test_cases += 1 + + images_info_cmd = f""" + kubectl get configmaps -n argo {activity_name} -o jsonpath='{{.data.iuf_activity}}' \ + | jq -r '.operation_outputs.stage_params["prepare-images"]["prepare-management-images"]["sat-bootprep-run"].script_stdout | fromjson | .images[] | "\(.name):\(.final_image_id)"' + """ + + images_name_and_ids, _ = run_command(images_info_cmd) + + if not images_name_and_ids: + print("ERROR: No images names and id found") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + test_cases = validate_images(images_name_and_ids, test_cases) + return test_cases + except subprocess.CalledProcessError as err: + print(f"Error: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + + +def main(): + """Calls every stage functions to execute the stage and test-cases""" + if len(sys.argv) < 3 or len(sys.argv) > 4: + print("Usage: script.py ") + sys.exit(1) + else: + test_cases = 0 + tar_dir = sys.argv[1] + activity_name = sys.argv[2] + print("TEST CASE: process-media stage") + test_cases = process_media(tar_dir, activity_name, test_cases) + print( + "----------------------- PROCESS-MEDIA tests completed -----------------------" + ) + + print("TEST CASES: pre-install-check stage") + test_cases = pre_install_check(activity_name, test_cases) + print( + "----------------------- PRE-INSTALL-CHECK tests completed -----------------------" + ) + + print("TEST CASES: deliver-product stage") + test_cases = deliver_product(activity_name, test_cases) + print( + "----------------------- DELIVER-PRODUCT tests completed -----------------------" + ) + + print("TEST CASES: update-vcs-config stage") + test_cases = update_vcs_config(activity_name, test_cases) + print( + "----------------------- UPDATE-VCS-CONFIG tests completed -----------------------" + ) + + print("TEST CASES: update_cfs_config stage") + test_cases = update_cfs_config(activity_name, test_cases) + print( + "----------------------- UPDATE-CFS-CONFIG tests completed -----------------------" + ) + + print("TEST CASES: prepare_images stage") + test_cases = prepare_images(activity_name, test_cases) + print( + "----------------------- PREPARE-IMAGES tests completed -----------------------" + ) + + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/iuf_stages/helper.py b/src/csm_testing/tests/iuf_stages/helper.py new file mode 100644 index 00000000..93615f4e --- /dev/null +++ b/src/csm_testing/tests/iuf_stages/helper.py @@ -0,0 +1,186 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +"""Helper functions for iuf_stages module""" +import subprocess +import sys +import json +import requests +from csm_testing.lib.iuf_common import ( + get_nexus_credentials, + run_command, +) +from csm_testing.lib.iuf_constants import ( + NEXUS_URL, ) + +REPO_URL = f"{NEXUS_URL}/service/rest/v1/repositories/yum/hosted" + + +def nexus_repo_setup(): + """creates a dummy-repo in nexus to upload the data from dummy-product installation.""" + repo_name = "dummy-repo" + username, password = get_nexus_credentials() + + repo_data = { + "name": repo_name, + "online": True, + "storage": { + "blobStoreName": "default", + "strictContentTypeValidation": True, + "writePolicy": "ALLOW", + }, + "cleanup": None, + "yum": { + "repodataDepth": 0, + "deployPolicy": "STRICT" + }, + "format": "yum", + "type": "hosted", + } + + response = requests.post( + REPO_URL, + auth=(username, password), + headers={"Content-Type": "application/json"}, + data=json.dumps(repo_data), + verify=False, + ) + + if response.status_code == 201: + print("INFO: Repository created successfully.") + elif response.status_code == 200: + print("INFO: Repository already exists.") + else: + print( + f"ERROR: Failed to create repository: {response.status_code} - {response.text}" + ) + sys.exit(1) + + +def check_product_data(dummy_product: dict): + """ + Checks if any of the essential product attributes are empty. + + Args: + dummy_product (dict): A dictionary representing the product data to be validated. + Expected keys: + - docker_images (list): A list of Docker image names or URIs. + - recipes (list): A list of recipe files or paths. + - loftsman_manifests (list): A list of Loftsman manifest files or paths. + - helm_charts (list): A list of Helm chart directories or paths. + + Returns: + bool: True if all product attributes are non-empty, False otherwise. + """ + if not dummy_product.docker_images: + print("ERROR: Docker images are empty!") + return False + if not dummy_product.recipes: + print("ERROR: Recipes are empty!") + return False + if not dummy_product.loftsman_manifests: + print("ERROR: Loftsman manifests are empty!") + return False + if not dummy_product.helm_charts: + print("ERROR: Helm charts are empty!") + return False + return True + + +def run_iuf_script(*args): + """Calls run_iuf_script to setup the media-dir and run process-media for dummy-product + + Returns: + bool: True if the media directory setup and iuf_run.py execution were successful. + False otherwise + """ + tar_dir = args[0] + activity_name = args[1] + try: + subprocess.run( + [ + "python3", + "/opt/cray/tests/install/ncn/scripts/python/iuf_run", + tar_dir, + activity_name, + ], + check=True, + ) + except subprocess.CalledProcessError as err: + print(f"ERROR: Error running iuf_run.py: {err}") + return False + return True + + +def validate_images(images_name_and_ids: str, test_cases: int): + """Validates images in IMS and boot-images artifacts + + Args: + images_name_and_ids (str): Image names and IDs as a newline-separated string + + Return: + int: number of executed test cases + """ + try: + image_lines = images_name_and_ids.splitlines() + + print("TEST CASE: Checking ims images...") + for image in image_lines: + name, final_image_id = image.split(":") + print( + f"INFO: Checking cray ims images for image Name: {name.strip()} " + f"with image ID: {final_image_id.strip()}") + check_ims_cmd = f"cray ims images describe {final_image_id.strip()}" + ims_info, _ = run_command(check_ims_cmd) + if not ims_info: + print(f"ERROR: Could not find ims image for {name}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + print(ims_info) + + test_cases += 1 + print("TEST CASE: Checking boot-images artifacts..") + for image in image_lines: + name, final_image_id = image.split(":") + print( + f"INFO: Checking cray artifacts boot-images for image Name: {name.strip()}" + ) + check_boot_images_cmd = f"cray artifacts describe boot-images {final_image_id.strip()}/manifest.json" + boot_images_info, _ = run_command(check_boot_images_cmd) + if not boot_images_info: + print(f"ERROR: Could not find boot-image for {name}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) + print(boot_images_info) + + test_cases += 1 + return test_cases + except subprocess.CalledProcessError as err: + print(f"Error: {err}") + print( + f"INFO: Total test cases executed for stage operations: {test_cases}" + ) + sys.exit(1) diff --git a/src/csm_testing/tests/iuf_stages_cleanup/__init__.py b/src/csm_testing/tests/iuf_stages_cleanup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/iuf_stages_cleanup/__main__.py b/src/csm_testing/tests/iuf_stages_cleanup/__main__.py new file mode 100644 index 00000000..d3a5d523 --- /dev/null +++ b/src/csm_testing/tests/iuf_stages_cleanup/__main__.py @@ -0,0 +1,185 @@ +# +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script will cleanup the all the data uploaded by IUF run of dummy-product. +""" + +import os +import sys +from requests import Session +import requests +from csm_testing.lib.iuf_constants import ( + PRODUCT_NAME, + PRODUCT_VERSION, + NEXUS_URL, +) +from csm_testing.lib.iuf_common import ( + get_nexus_credentials, + vcs_auth, + run_command, +) + +DELETION_FILE_PATH = "/etc/cray/upgrade/csm/iuf/deletion" +IMAGE = "registry.local/artifactory.algol60.net/csm-docker/stable/product-deletion-utility:1.0.1" + + +def cleanup_deliver_product(): + """ + Cleans up the data uploaded, repository created while running deliver-product stage + using dummy-product + Args: None + Returns: None + """ + + repo_name = "dummy-repo" + username, password = get_nexus_credentials() + + print(f"INFO: Deleting repository '{repo_name}' in Nexus...") + delete_url = f"{NEXUS_URL}/service/rest/v1/repositories/{repo_name}" + + response = requests.delete(delete_url, auth=(username, password)) + if response.status_code == 204: + print("INFO: Repository deleted successfully.") + else: + print( + f"ERROR: Failed to delete repository: {response.status_code} - {response.text}" + ) + sys.exit(1) + + if not os.path.exists(DELETION_FILE_PATH): + with open(DELETION_FILE_PATH, "w", encoding="utf-8"): + pass + else: + pass + + pod_command = f""" + podman run --rm \ + --mount type=bind,src=/etc/kubernetes/admin.conf,target=/root/.kube/config,ro=true \ + --mount type=bind,src=/var/lib/ca-certificates,target=/var/lib/ca-certificates,ro=true \ + --mount type=bind,src=/etc/cray/upgrade/csm/iuf/deletion,target=/etc/cray/upgrade/csm/iuf/deletion,ro=false \ + {IMAGE} delete {PRODUCT_NAME} {PRODUCT_VERSION} \ + """ + print(f"INFO: Running podman command: {pod_command}") + run_command(pod_command) + + remove_dummy_entry = ( + "kubectl patch configmap cray-product-catalog -n services --type merge -p " + '\'{"data":{"dummy":null}}\'') + + print("INFO: Removing dummy entry from cray-product-catalog ConfigMap...") + run_command(remove_dummy_entry) + + # Delete the cray-product-catalog-dummy ConfigMap + delete_dummy_cm = "kubectl delete configmap cray-product-catalog-dummy -n services" + print( + "INFO: Deleting cray-product-catalog-dummy ConfigMap in services namespace..." + ) + run_command(delete_dummy_cm) + + if os.path.exists(DELETION_FILE_PATH): + print(f"INFO: Removing deletion file: {DELETION_FILE_PATH}") + os.remove(DELETION_FILE_PATH) + else: + print( + f"INFO: Deletion file {DELETION_FILE_PATH} not found, skipping deletion." + ) + + print("INFO: Cleanup complete for DELIVER-PRODUCT stage!") + + +def cleanup_vcs_repo(): + """ + Deleting the branch created while running update-vcs-config for dummy-product + Args: None + Returns: None + """ + + session = Session() + dummy_repo_url, auth, ca_cert_path = vcs_auth() + + print(f"INFO: Attempting to delete Gitea repository: {dummy_repo_url}") + resp = session.delete(dummy_repo_url, verify=ca_cert_path, auth=auth) + + if resp.status_code == 204: + print(f"INFO: Repository '{dummy_repo_url}' deleted successfully.") + elif resp.status_code == 404: + print(f"WARNING: Repository '{dummy_repo_url}' not found.") + else: + print( + f"ERROR: Failed to delete repository: {resp.status_code}, {resp.text}" + ) + sys.exit(1) + + if os.path.exists(ca_cert_path): + print(f"INFO: Removing certificate file: {ca_cert_path}") + os.remove(ca_cert_path) + else: + print( + f"INFO: Certificate file {ca_cert_path} not found, skipping deletion." + ) + print("INFO: Cleanup complete for UPDATE-VCS-CONFIG stage!") + + +def cleanup_cfs_configurations(): + """ + Cleanup the configurations created by IUf for dummy-product + Args: None + Returns: None + """ + cfs_delete_command = ( + "cray cfs configurations delete config-minimal-management-dummy-1.0.0") + run_command(cfs_delete_command) + print("INFO: Cleanup complete for UPDATE-CFS-CONFIG stage!") + + +def cleanup_prepared_images(): + """ + Cleanup the images created by IUf for dummy-product + Args: None + Returns: None + """ + print( + "INFO: The ims images and s3 artifacts already deleted by product-deletion-utility" + ) + print("INFO: Cleanup complete for PREPARE-IMAGES stage!") + + +def main(): + """ + Calls stage by stage cleanup functions for IUF + Args: None + Returns: None + """ + print("---------------STARTING CLEANUP FOR STAGE OPERATIONS--------------") + cleanup_deliver_product() + cleanup_vcs_repo() + cleanup_cfs_configurations() + cleanup_prepared_images() + print( + "--------------- CLEANUP FOR STAGE OPERATIONS COMPLETED --------------" + ) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/sat_validate_bootprep/__init__.py b/src/csm_testing/tests/sat_validate_bootprep/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/sat_validate_bootprep/__main__.py b/src/csm_testing/tests/sat_validate_bootprep/__main__.py new file mode 100644 index 00000000..57568693 --- /dev/null +++ b/src/csm_testing/tests/sat_validate_bootprep/__main__.py @@ -0,0 +1,141 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script validates bootprep file. +""" + +import sys +import subprocess +import os +import yaml +from jsonschema import validate, ValidationError, SchemaError + + +class BootPrepValidationError(Exception): + """Custom exception for BootPrep validation errors.""" + + +class ManifestValidationError(Exception): + """Custom exception for validation errors.""" + + +def load_yaml(file_path): + """Load a YAML file and return the parsed data.""" + try: + with open(file_path, "r", encoding="utf-8") as file: + return yaml.safe_load(file) + except yaml.YAMLError as err: + raise ManifestValidationError( + f"ERROR: Error loading YAML file {file_path}: {err}" + ) from err + except FileNotFoundError as err: + raise ManifestValidationError( + f"ERROR: File not found: {file_path}: {err}" + ) from err + except Exception as err: + raise ManifestValidationError( + f"ERROR: Error reading file {file_path}: {err}" + ) from err + + +def validate_instance(instance, schema): + """Validate the instance data against the schema.""" + try: + validate(instance=instance, schema=schema) + except ValidationError as err: + raise ManifestValidationError(f"ERROR: Validation failed: {err}") from err + except SchemaError as err: + raise ManifestValidationError(f"ERROR: Schema error: {err}") from err + + +def get_bootprep_schema(): + """ + Retrieve the bootprep schema using the 'sat bootprep view-schema' command. + Returns: + dict: Parsed schema YAML as a dictionary. + Raises: + BootPrepValidationError: If the command fails or returns invalid YAML. + """ + try: + result = subprocess.run( + ["sat", "bootprep", "view-schema"], + check=True, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + print("INFO: Schema retrieved successfully using 'sat bootprep view-schema'.") + yaml_output = yaml.safe_load(result.stdout) + return yaml_output + except subprocess.CalledProcessError as err: + raise BootPrepValidationError( + f"ERROR: Failed to execute 'sat bootprep view-schema'. {err.stderr}" + ) from err + except Exception as err: + raise BootPrepValidationError(f"ERROR: Failed to parse schema. {err}") from err + + +def main(): + """ + The main entry point of the program. + Args: + None + Return: + None + """ + if len(sys.argv) != 2: + print("Usage: validate_bootprep.py ") + sys.exit(1) + + bootprep_file = sys.argv[1] + # Fetch the schema + try: + schema = get_bootprep_schema() + except BootPrepValidationError as err: + print(err) + sys.exit(1) + + # Load the bootprep file + try: + if os.path.exists(bootprep_file): + bootprep_instance = load_yaml(bootprep_file) + print(f"INFO: Bootprep file '{bootprep_file}' loaded successfully.") + else: + print(f"{bootprep_file} : FileNotFoundError") + sys.exit(1) + except BootPrepValidationError as err: + print(f"{err}") + sys.exit(1) + + # Validate the bootprep file against the schema + try: + validate_instance(bootprep_instance, schema) + print(f"SUCCESS: Bootprep file '{bootprep_file}' is valid against the schema.") + print("SUCCESS: Test Case Passed") + except BootPrepValidationError as err: + print(f"{err}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/src/csm_testing/tests/validate_iuf_manifest/__init__.py b/src/csm_testing/tests/validate_iuf_manifest/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csm_testing/tests/validate_iuf_manifest/__main__.py b/src/csm_testing/tests/validate_iuf_manifest/__main__.py new file mode 100644 index 00000000..c2593dbd --- /dev/null +++ b/src/csm_testing/tests/validate_iuf_manifest/__main__.py @@ -0,0 +1,99 @@ +# MIT License +# +# (C) Copyright 2024 Hewlett Packard Enterprise Development LP +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +""" +This script validates IUF manifest. +""" + +import sys +import subprocess +import os + + +def validate_manifest_with_podman(manifest_file, cray_nls_image): + """ + Validate the manifest file using the podman command. + Args: + manifest_file (str): Path to the manifest file + cray_nls_image (str): CRAY_NLS_IMAGE to use for validation + Returns: + None + Raises: + RuntimeError: If podman command fails + """ + try: + # Ensure the manifest file exists + if not os.path.exists(manifest_file): + print(f"ERROR: Manifest file '{manifest_file}' not found.") + sys.exit(1) + + manifest_basename = os.path.basename(manifest_file) + + # Construct the podman command + cmd = [ + "podman", + "run", + "--rm", + "--userns", + "keep-id", + "-v", + f"{os.path.realpath(manifest_file)}:/{manifest_basename}", + cray_nls_image, + "validate", + f"/{manifest_basename}", + ] + + print(f"INFO: Running podman command for validation:\n{' '.join(cmd)}") + + # Run the podman command + subprocess.run(cmd, check=True) + print("INFO: SUCCESS: IUF product manifest file is valid.") + except subprocess.CalledProcessError as err: + raise RuntimeError(f"ERROR: Schema validation failed: {err}") from err + + +def main(): + """ + Main entry point for the program. + """ + print("Test Case: validate_iuf_product_manifest") + if len(sys.argv) != 2: + print("Usage: validate_product_manifest.py ") + sys.exit(1) + + manifest_file = sys.argv[1] + + try: + cray_nls_image = ( + "arti.hpc.amslabs.hpecorp.net/csm-docker-remote/stable/cray-nls:0.10.0" + ) + + # Validate the manifest file using podman + validate_manifest_with_podman(manifest_file, cray_nls_image) + + except (RuntimeError, ValueError) as err: + print(err) + sys.exit(1) + + +if __name__ == "__main__": + main()