diff --git a/.github/workflows/pre-main.yaml b/.github/workflows/pre-main.yaml index 60888ff..45888ef 100644 --- a/.github/workflows/pre-main.yaml +++ b/.github/workflows/pre-main.yaml @@ -72,7 +72,7 @@ jobs: - name: Typos uses: crate-ci/typos@master - + - name: Yamllint uses: ibiqlik/action-yamllint@v3 with: @@ -80,3 +80,76 @@ jobs: - name: Go vet run: make vet + + test: + name: Build, install and test operator in a Kind cluster + runs-on: ubuntu-22.04 + env: + SHELL: /bin/bash + KUBECONFIG: '/home/runner/.kube/config' + + steps: + - name: Disable default go problem matcher + run: echo "::remove-matcher owner=go::" + + - name: Check out code into the Go module directory + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + + - name: Build operator + run: ./scripts/ci/build.sh + + - name: Removed unused docker images and go cache cleanup + run: | + df -h + docker rmi $(docker images -f "dangling=true" -q) || true + docker builder prune --all -f + go clean -modcache + df -h + + # Create a Kind cluster for testing. + - name: Check out `cnf-certification-test-partner` repo + uses: actions/checkout@v4 + with: + repository: test-network-function/cnf-certification-test-partner + path: cnf-certification-test-partner + + - name: Bootstrap cluster, docker, and python + uses: nick-fields/retry@v2 + with: + timeout_minutes: 5 + max_attempts: 3 + command: cd ${GITHUB_WORKSPACE}/cnf-certification-test-partner; make bootstrap-cluster && make bootstrap-docker-ubuntu-local && make bootstrap-python-ubuntu-local + + - name: Run 'make rebuild-cluster' + uses: nick-fields/retry@v2 + env: + SKIP_PRELOAD_IMAGES: true + with: + timeout_minutes: 15 + max_attempts: 3 + command: cd ${GITHUB_WORKSPACE}/cnf-certification-test-partner; make rebuild-cluster + + - name: Run 'make install' + uses: nick-fields/retry@v2 + env: + SKIP_PRELOAD_IMAGES: true + with: + timeout_minutes: 20 + max_attempts: 3 + command: cd ${GITHUB_WORKSPACE}/cnf-certification-test-partner; make install + + - name: More cleanup + run: | + df -h + docker rmi $(docker images -f "dangling=true" -q) || true + docker builder prune --all -f + go clean -modcache + df -h + + - name: Install operator in the Kind cluster + run: ./scripts/ci/deploy.sh + + - name: Run simple smoke test. + run: ./scripts/ci/smoke_test.sh diff --git a/bundle/manifests/tnf-op.clusterserviceversion.yaml b/bundle/manifests/tnf-op.clusterserviceversion.yaml index 6dfba15..df778aa 100644 --- a/bundle/manifests/tnf-op.clusterserviceversion.yaml +++ b/bundle/manifests/tnf-op.clusterserviceversion.yaml @@ -16,7 +16,7 @@ metadata: "app.kubernetes.io/part-of": "tnf-op" }, "name": "cnfcertificationsuiterun-sample", - "namespace": "cnf-certification-operator" + "namespace": "cnf-certsuite-operator" }, "spec": { "configMapName": "cnf-certsuite-config", @@ -28,10 +28,10 @@ metadata: } ] capabilities: Basic Install - createdAt: "2024-01-12T12:33:33Z" + createdAt: "2024-01-25T07:40:11Z" operators.operatorframework.io/builder: operator-sdk-v1.33.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 - name: tnf-op.v0.0.3 + name: tnf-op.v0.0.1 namespace: placeholder spec: apiservicedefinitions: {} @@ -157,8 +157,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.annotations['olm.targetNamespaces'] - image: quay.io/greyerof/cnf-op:initialtestv5 - imagePullPolicy: Always + - name: SIDECAR_APP_IMG + value: quay.io/testnetworkfunction/cnf-certsuite-operator-sidecar:v0.0.1 + image: quay.io/testnetworkfunction/cnf-certsuite-operator:v0.0.1 + imagePullPolicy: IfNotPresent livenessProbe: httpGet: path: /healthz @@ -307,4 +309,4 @@ spec: maturity: alpha provider: name: RedHat - version: 0.0.3 + version: 0.0.1 diff --git a/cnf-cert-sidecar/Dockerfile b/cnf-cert-sidecar/Dockerfile index 479a023..55ddea1 100644 --- a/cnf-cert-sidecar/Dockerfile +++ b/cnf-cert-sidecar/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/ubi:8.9-1028 AS build +FROM registry.access.redhat.com/ubi8/ubi:8.9-1107.1705420509 AS build # Install dependencies # hadolint ignore=DL3041 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 1afcba0..d82c345 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,5 +1,5 @@ # Adds namespace to all resources. -namespace: cnf-certification-operator +namespace: cnf-certsuite-operator # Value of this field is prepended to the # names of all resources, e.g. a deployment named diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 220e8d2..fe6594a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/greyerof/cnf-op - newTag: initialtestv5 + newName: quay.io/testnetworkfunction/cnf-certsuite-operator + newTag: v0.0.1 + diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 1bd1190..2d785f4 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -75,9 +75,11 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: SIDECAR_APP_IMG + value: quay.io/testnetworkfunction/cnf-certsuite-operator-sidecar:v0.0.1 image: controller:latest name: manager - imagePullPolicy: Always + imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 68cc5d7..23ef389 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -9,5 +9,5 @@ resources: #+kubebuilder:scaffold:manifestskustomizesamples -namespace: cnf-certification-operator +namespace: cnf-certsuite-operator diff --git a/controllers/cnf-cert-job/cnfcertjob.go b/controllers/cnf-cert-job/cnfcertjob.go index bd595c5..5e5aa3a 100644 --- a/controllers/cnf-cert-job/cnfcertjob.go +++ b/controllers/cnf-cert-job/cnfcertjob.go @@ -22,9 +22,10 @@ type Config struct { LogLevel string ConfigMapName string PreflightSecretName string + SideCarAppImage string } -func NewConfig(podName, namespace, certSuiteConfigRunName, labelsFilter, logLevel, configMapName, preflightSecretName string) *Config { +func NewConfig(podName, namespace, certSuiteConfigRunName, labelsFilter, logLevel, configMapName, preflightSecretName, sideCarAppImage string) *Config { return &Config{ PodName: podName, Namespace: namespace, @@ -33,6 +34,7 @@ func NewConfig(podName, namespace, certSuiteConfigRunName, labelsFilter, logLeve LogLevel: logLevel, ConfigMapName: configMapName, PreflightSecretName: preflightSecretName, + SideCarAppImage: sideCarAppImage, } } @@ -50,7 +52,7 @@ func New(config *Config) *corev1.Pod { Containers: []corev1.Container{ { Name: definitions.CnfCertSuiteSidecarContainerName, - Image: "quay.io/greyerof/cnf-op:sidecarv4", + Image: config.SideCarAppImage, Env: []corev1.EnvVar{ { Name: "MY_POD_NAME", @@ -77,7 +79,7 @@ func New(config *Config) *corev1.Pod { Value: config.CertSuiteConfigRunName, }, }, - ImagePullPolicy: "Always", + ImagePullPolicy: "IfNotPresent", VolumeMounts: []corev1.VolumeMount{ { Name: "cnf-certsuite-output", diff --git a/controllers/cnfcertificationsuiterun_controller.go b/controllers/cnfcertificationsuiterun_controller.go index bccbd28..d4ca59c 100644 --- a/controllers/cnfcertificationsuiterun_controller.go +++ b/controllers/cnfcertificationsuiterun_controller.go @@ -19,6 +19,7 @@ package controllers import ( "context" "fmt" + "os" "time" corev1 "k8s.io/api/core/v1" @@ -39,6 +40,8 @@ import ( "github.com/sirupsen/logrus" ) +var sideCarImage string + // CnfCertificationSuiteRunReconciler reconciles a CnfCertificationSuiteRun object type CnfCertificationSuiteRunReconciler struct { client.Client @@ -239,7 +242,8 @@ func (r *CnfCertificationSuiteRunReconciler) Reconcile(ctx context.Context, req cnfrun.Spec.LabelsFilter, cnfrun.Spec.LogLevel, cnfrun.Spec.ConfigMapName, - cnfrun.Spec.PreflightSecretName) + cnfrun.Spec.PreflightSecretName, + sideCarImage) cnfCertJobPod := cnfcertjob.New(config) err := r.Create(ctx, cnfCertJobPod) @@ -260,6 +264,12 @@ func (r *CnfCertificationSuiteRunReconciler) SetupWithManager(mgr ctrl.Manager) logrus.Infof("Setting up CnfCertificationSuiteRunReconciler's manager.") certificationRuns = map[certificationRun]string{} + var found bool + sideCarImage, found = os.LookupEnv(definitions.SideCarImageEnvVar) + if !found { + return fmt.Errorf("sidecar app img env var %q not found", definitions.SideCarImageEnvVar) + } + return ctrl.NewControllerManagedBy(mgr). For(&cnfcertificationsv1alpha1.CnfCertificationSuiteRun{}). WithEventFilter(ignoreUpdatePredicate()). diff --git a/controllers/definitions/definitions.go b/controllers/definitions/definitions.go index a86f640..739e1d7 100644 --- a/controllers/definitions/definitions.go +++ b/controllers/definitions/definitions.go @@ -14,4 +14,6 @@ const ( PreflightDockerConfigFilePath = CnfPreflightConfigFolder + "/preflight_dockerconfig.json" SideCarResultsFolderEnvVar = "TNF_RESULTS_FOLDER" + + SideCarImageEnvVar = "SIDECAR_APP_IMG" ) diff --git a/hack/deleteall.sh b/hack/deleteall.sh deleted file mode 100755 index b9f9c15..0000000 --- a/hack/deleteall.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -deleteAll() { - res=$1 - for name in $(oc get "$res" -n cnf-certification-operator -o json | jq -r '.items[] | .metadata.name' | sort -r) ; do oc delete "$res" -n cnf-certification-operator "$name" ; done -} - -deleteAll cnfcertificationsuiteruns -deleteAll cnfcertificationsuitereports -deleteAll pods - -oc get all -n cnf-certification-operator diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh new file mode 100755 index 0000000..f7061a9 --- /dev/null +++ b/scripts/ci/build.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# +# Builds the operator's controller and sidecar images. The manager's env var +# with the sidecar app image is also replaced with the new name, which is set +# using the value from env var SIDECAR_IMG. It will be defaulted to DEFAULT_SIDECAR_IMG +# if that env var is not found. +# +# This script is intended to be used in CI workflows to create loca/test images just for +# testing purposes, but can also be used to generate images for new official releases. In +# that case, the following env vars should have been exported before running this script: +# VERSION : The desired new/test version tag that will be applied to both images. +# IMG : The image for the controller's container. +# + +# Bash settings: display (expanded) commands and fast exit on first error. +set -o xtrace +set -o errexit + +DEFAULT_TEST_VERSION="0.0.1-test" +DEFAULT_SIDECAR_IMG="local-test-sidecar-image:v$DEFAULT_TEST_VERSION" +DEFAULT_IMG="ci-cnf-op:v$DEFAULT_TEST_VERSION" + +export VERSION="${VERSION:-$DEFAULT_TEST_VERSION}" +export SIDECAR_IMG="${SIDECAR_IMG:-$DEFAULT_SIDECAR_IMG}" +export IMG="${IMG:-$DEFAULT_IMG}" + +# step: Build manifests and controller app +make build + +# step: Run tests +make test + +# step: Build sidecar app +docker build --no-cache -t "${SIDECAR_IMG}" -f cnf-cert-sidecar/Dockerfile . + +# Local install kustomize app that is needed to edit/patch the kustomization.yaml +make kustomize + +# step: Add env var to the controller's container to set the sidecar image app that was built right before +pushd config/manager + ../../bin/kustomize edit add patch --kind Deployment --patch "[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/env/1\", \"value\": {\"name\": \"SIDECAR_APP_IMG\", \"value\": \"${SIDECAR_IMG}\"} }]" +popd + +# step: Build docker image for the controller. This will use IMG pointing to local docker image for the controller, which +# will be pushed to kind with "docker load docker-image $IMG" +make docker-build diff --git a/scripts/ci/deploy.sh b/scripts/ci/deploy.sh new file mode 100755 index 0000000..90a731b --- /dev/null +++ b/scripts/ci/deploy.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# This script deploys a recently built operator in a kind cluster. +# Both the operator's controller and the sidecar app images are +# preloaded into the kind cluster's nodes to avoid the need of +# uploading the images to an external registry (quay/docker). +# +# The operator is deployed in the namespace set by env var CNF_CERTSUITE_OPERATOR_NAMESPACE +# or in the defaulted namespace "cnf-certsuite-operator" if that env var is not found. +# + +# Bash settings: display (expanded) commands and fast exit on first error. +set -o xtrace +set -o errexit + +DEFAULT_CNF_CERTSUITE_OPERATOR_NAMESPACE="cnf-certsuite-operator" +DEFAULT_TEST_VERSION="0.0.1-test" +DEFAULT_SIDECAR_IMG="local-test-sidecar-image:v$DEFAULT_TEST_VERSION" +DEFAULT_IMG="ci-cnf-op:v$DEFAULT_TEST_VERSION" + +CNF_CERTSUITE_OPERATOR_NAMESPACE=${CNF_CERTSUITE_OPERATOR_NAMESPACE:-$DEFAULT_CNF_CERTSUITE_OPERATOR_NAMESPACE} + +export VERSION="${VERSION:-$DEFAULT_TEST_VERSION}" +export SIDECAR_IMG="${SIDECAR_IMG:-$DEFAULT_SIDECAR_IMG}" +export IMG="${IMG:-$DEFAULT_IMG}" + +kind load docker-image "${SIDECAR_IMG}" +kind load docker-image "${IMG}" + +# "make deploy" uses the IMG env var internally, and it needs to be exported. +# let's patch the installation namespace. +make kustomize + +pushd config/default + ../../bin/kustomize edit set namespace "${CNF_CERTSUITE_OPERATOR_NAMESPACE}" +popd + +make deploy + +# step: Wait for the controller's containers to be ready +oc wait --for=condition=ready pod --all=true -n cnf-certsuite-operator --timeout=2m \ No newline at end of file diff --git a/scripts/ci/resource_exists.sh b/scripts/ci/resource_exists.sh new file mode 100755 index 0000000..3dfb81b --- /dev/null +++ b/scripts/ci/resource_exists.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# WARNING: This is a helper script to be run with the "timeout" command, like: +# $ timeout 60s $0 pod mypodname mynamespace +# +# Polls the cluster with "oc get ..." every N secs and returns with 0 status +# code as soon as the given resource by name is found in a given namespace. +# In case the resource is not found or there's any problem with "oc get", the +# script will never return, as it's polling every N secs, no matter the type of +# error was returned. +# +# $1 resource kind, e.g. pod, deployment... +# $2 name +# $3 namespace +# $4 check interval time (optional, defaults to 5 segs) +# +# Examples: +# $0 pod mypodname mypodnamespace + +DEFAULT_INTERVAL_CHECK_SEGS=5 +INTERVAL_CHECK_SEGS=${DEFAULT_INTERVAL_CHECK_SEGS} + +RESOURCE_KIND=$1 +RESOURCE_NAME=$2 +NAMESPACE=$3 + +if [ "$4" != "" ] ; then + INTERVAL_CHECK_SEGS=$4 +fi + +echo "Polling every ${INTERVAL_CHECK_SEGS} secs for ${RESOURCE_NAME} (kind: ${RESOURCE_KIND}) to exist in namespace ${NAMESPACE}" + +while true; do + if oc get "${RESOURCE_KIND}" -n "${NAMESPACE}" "${RESOURCE_NAME}" ; then + echo "${RESOURCE_NAME} (kind: ${RESOURCE_KIND}) found in namespace ${NAMESPACE}." + exit 0 + fi + + echo "${RESOURCE_NAME} (kind: ${RESOURCE_KIND}) not found yet in namespace ${NAMESPACE}..." + sleep "${INTERVAL_CHECK_SEGS}" +done diff --git a/scripts/ci/smoke_test.sh b/scripts/ci/smoke_test.sh new file mode 100755 index 0000000..742ec03 --- /dev/null +++ b/scripts/ci/smoke_test.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# +# This is a very simple functional test the controller and the expected +# CnfCertificationSuiteReport CR that should be generated. +# +# Applies the CnfCertificatioSuiteRun sample in config/samples along with its +# related configmap and (preflight) secret. The script waits for the report CR +# (CnfCertificationSuiteReport) to exist in the operator's namespace and then +# checks whether the verdict and the summary counters to have the expected +# values. The expected values can be customized by exporting their env vars +# before running the script. +# +# A patch to the kustomize.yaml file in config/samples needs to be done to add +# the configmap and the secret. +# + +# Bash settings: display (expanded) commands and fast exit on first error. +set -o xtrace +set -o errexit + +DEFAULT_CNF_CERTSUITE_OPERATOR_NAMESPACE="cnf-certsuite-operator" +CNF_CERTSUITE_OPERATOR_NAMESPACE=${CNF_CERTSUITE_OPERATOR_NAMESPACE:-$DEFAULT_CNF_CERTSUITE_OPERATOR_NAMESPACE} + +# Load samples, patching the kustomization.yaml to include the configmap and the preflight dockerconfig. +pushd config/samples + ../../bin/kustomize edit add resource "extra/cnf-certsuite-configmap.yaml" + ../../bin/kustomize edit add resource "extra/cnf-certsuite-preflight-secret.yaml" +popd + +# Apply/create the sample CR. +oc kustomize config/samples | oc apply -f - + +# Wait for the CnfCertificationSuiteReport CR to appear in the operator's namespace. +timeout 120s ./scripts/ci/resource_exists.sh cnfcertificationsuitereports cnf-job-run-1-report "${CNF_CERTSUITE_OPERATOR_NAMESPACE}" + +# Check that a cnfcertificationsuitereport CR has been created. +# Disable command expansion to avoid output mangling. +set +o xtrace + +# Save the report CR in JSON. +reportJson=$(oc get cnfcertificationsuitereports -n "${CNF_CERTSUITE_OPERATOR_NAMESPACE}" cnf-job-run-1-report -o json) + +# Show the report CR for debugging purposes. +echo "$reportJson" | jq + +# Run checks for verdict and counters. +export EXPECTED_VERDICT=${EXPECTED_VERDICT:-"pass"} +export EXPECTED_TOTAL_TCS=${EXPECTED_TOTAL_TCS:-"88"} +export EXPECTED_FAILED=${EXPECTED_FAILED:-"0"} +export EXPECTED_PASSED=${EXPECTED_PASSED:-"4"} +export EXPECTED_SKIPPED=${EXPECTED_SKIPPED:-"84"} + +# Check the verdit is pass +echo "$reportJson" | jq 'if .status.verdict == env.EXPECTED_VERDICT then "verdict is "+env.EXPECTED_VERDICT else error("verdict mismatch: \(.status.verdict), expected "+env.EXPECTED_VERDICT) end' +echo "$reportJson" | jq 'if .status.summary.total | tostring == env.EXPECTED_TOTAL_TCS then "total tc num is ok" else error("total tcs mismatch: \(.status.summary.total), expected "+env.EXPECTED_TOTAL_TCS) end' +echo "$reportJson" | jq 'if .status.summary.passed | tostring == env.EXPECTED_PASSED then "passed tc num is ok" else error("passed tcs mismatch: \(.status.summary.passed), expected "+env.EXPECTED_PASSED) end' +echo "$reportJson" | jq 'if .status.summary.skipped | tostring == env.EXPECTED_SKIPPED then "skipped tc num is ok" else error("skipped tcs mismatch: \(.status.summary.skipped), expected "+env.EXPECTED_SKIPPED) end' +echo "$reportJson" | jq 'if .status.summary.failed | tostring == env.EXPECTED_FAILED then "failed tc num is ok" else error("failed tcs mismatch: \(.status.summary.failed), expected "+env.EXPECTED_FAILED) end'