diff --git a/.ci-operator.yaml b/.ci-operator.yaml index a65cf6c1..83f6066c 100644 --- a/.ci-operator.yaml +++ b/.ci-operator.yaml @@ -1,4 +1,4 @@ build_root_image: name: boilerplate namespace: openshift - tag: image-v3.0.2 + tag: image-v3.0.3 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 92e7d171..d4f250ce 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,3 +10,5 @@ updates: ignore: - dependency-name: "app-sre/boilerplate" # don't upgrade boilerplate via these means + - dependency-name: "openshift/origin-operator-registry" + # don't upgrade origin-operator-registry via these means diff --git a/boilerplate/_data/backing-image-tag b/boilerplate/_data/backing-image-tag index e82f404b..8b5f6fb5 100644 --- a/boilerplate/_data/backing-image-tag +++ b/boilerplate/_data/backing-image-tag @@ -1 +1 @@ -image-v3.0.2 +image-v3.0.3 diff --git a/boilerplate/_data/last-boilerplate-commit b/boilerplate/_data/last-boilerplate-commit index e718902f..e1650ef2 100644 --- a/boilerplate/_data/last-boilerplate-commit +++ b/boilerplate/_data/last-boilerplate-commit @@ -1 +1 @@ -f2283ca508e2f9af2e1f1d08e86051aefdad0386 +764036b2bf3138ef860fa07d8e0cc017c8cfca43 diff --git a/boilerplate/_lib/freeze-check b/boilerplate/_lib/freeze-check index 88c91bbd..7fb18ec3 100755 --- a/boilerplate/_lib/freeze-check +++ b/boilerplate/_lib/freeze-check @@ -70,9 +70,9 @@ cd $REPO_ROOT BOILERPLATE_GIT_CLONE="git clone $TMPD" boilerplate/update # Okay, if anything has changed, that's bad. -if [[ $(git status --porcelain | wc -l) -ne 0 ]]; then +if [[ $(git status --porcelain -- ':!build/Dockerfile*' | wc -l) -ne 0 ]]; then echo "Your boilerplate is dirty!" >&2 - git status --porcelain + git status --porcelain -- ':!build/Dockerfile*' exit 1 fi diff --git a/boilerplate/generated-includes.mk b/boilerplate/generated-includes.mk index 78b982f6..4e424c94 100644 --- a/boilerplate/generated-includes.mk +++ b/boilerplate/generated-includes.mk @@ -5,3 +5,5 @@ include boilerplate/_lib/boilerplate.mk include boilerplate/openshift/golang-osd-operator/csv-generate/csv-generate.mk include boilerplate/openshift/golang-osd-operator/project.mk include boilerplate/openshift/golang-osd-operator/standard.mk +include boilerplate/openshift/golang-osd-operator-osde2e/project.mk +include boilerplate/openshift/golang-osd-operator-osde2e/standard.mk diff --git a/boilerplate/openshift/golang-osd-operator-osde2e/README.md b/boilerplate/openshift/golang-osd-operator-osde2e/README.md new file mode 100644 index 00000000..498b2f53 --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator-osde2e/README.md @@ -0,0 +1,40 @@ +# Conventions for Ginkgo based e2e tests + +- [Conventions for Ginkgo based e2e tests](#conventions-for-ginkgo-based-e2e-tests) + - [Consuming](#consuming) + - [`make` targets and functions.](#make-targets-and-functions) + - [E2E Test Harness](#e2e-test-harness) + - [Local Testing](#e2e-harness-local-testing) + +## Consuming +Currently, this convention is only intended for OSD operators. To adopt this convention, your `boilerplate/update.cfg` should include: + +``` +openshift/golang-osd-operator-osde2e +``` + +## `make` targets and functions. + +**Note:** Your repository's main `Makefile` needs to be edited to include the +"nexus makefile include": + +``` +include boilerplate/generated-includes.mk +``` + +One of the primary purposes of these `make` targets is to allow you to +standardize your prow and app-sre pipeline configurations using the +following: + +### E2e Test Harness + +| `make` target | Purpose | +|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `e2e-harness-generate` | Generate scaffolding for an end to end test harness. The `osde2e/` directory is created where the tests and test runner reside. The harness has access to cloud client and addon passthrough secrets within the test job cluster. Add your operator related ginkgo e2e tests under the `osde2e/_tests.go` file. See [this README](https://github.com/openshift/osde2e-example-test-harness/blob/main/README.md#locally-running-this-example) for more details on test harness. | +| `e2e-harness-build`| Compiles ginkgo tests under osde2e/tests and creates the binary to be used by docker image used by osde2e. | +| `e2e-image-build-push` | Builds osde2e test harness image and pushes to operator's quay repo. Image name is defaulted to -test-harness. Quay repository must be created beforehand. | + +#### E2E Harness Local Testing + +Please follow [this README](https://github.com/openshift/osde2e-example-test-harness/blob/main/README.md#locally-running-this-example) to test your e2e harness with Osde2e locally + diff --git a/boilerplate/openshift/golang-osd-operator-osde2e/e2e-harness-generate.sh b/boilerplate/openshift/golang-osd-operator-osde2e/e2e-harness-generate.sh new file mode 100755 index 00000000..8bd83c09 --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator-osde2e/e2e-harness-generate.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +set -e + +cmd=${0##*/} + +usage() { + cat < $HARNESS_DIR/Dockerfile + +echo "package osde2etests + +import \"github.com/onsi/ginkgo/v2\" + +var _ = ginkgo.Describe(\"$OPERATOR_NAME\", func() { + // Add your tests +}) +" > ${HARNESS_DIR}/${OPERATOR_HYPHEN_NAME}_tests.go + +echo "// THIS FILE IS GENERATED BY BOILERPLATE. DO NOT EDIT. +//go:build integration +// +build integration + +package osde2etests + +import ( + \"path/filepath\" + \"testing\" + . \"github.com/onsi/ginkgo/v2\" + . \"github.com/onsi/gomega\" +) + +const ( + testResultsDirectory = \"/test-run-results\" + jUnitOutputFilename = \"junit-example-addon.xml\" +) + +// Test entrypoint. osde2e runs this as a test suite on test pod. +func Test$REPLACE_FUNC(t *testing.T) { + RegisterFailHandler(Fail) + + suiteConfig, reporterConfig := GinkgoConfiguration() + reporterConfig.JUnitReport = filepath.Join(testResultsDirectory, jUnitOutputFilename) + RunSpecs(t, \"$REPLACE_SPECNAME\", suiteConfig, reporterConfig) + +} +" > ${HARNESS_DIR}/${OPERATOR_HYPHEN_NAME}_runner_test.go diff --git a/boilerplate/openshift/golang-osd-operator-osde2e/e2e-image-build-push.sh b/boilerplate/openshift/golang-osd-operator-osde2e/e2e-image-build-push.sh new file mode 100755 index 00000000..b0872792 --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator-osde2e/e2e-image-build-push.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -ev + +usage() { + cat < Podman uses --authfile=PATH *after* the `login` subcommand; but +# also accepts REGISTRY_AUTH_FILE from the env. See +# https://www.mankier.com/1/podman-login#Options---authfile=path +export REGISTRY_AUTH_FILE = ${CONTAINER_ENGINE_CONFIG_DIR}/config.json +# If this configuration file doesn't exist, podman will error out. So +# we'll create it if it doesn't exist. +ifeq (,$(wildcard $(REGISTRY_AUTH_FILE))) +$(shell mkdir -p $(CONTAINER_ENGINE_CONFIG_DIR)) +$(shell echo '{}' > $(REGISTRY_AUTH_FILE)) +endif +# ==> Docker uses --config=PATH *before* (any) subcommand; so we'll glue +# that to the CONTAINER_ENGINE variable itself. (NOTE: I tried half a +# dozen other ways to do this. This was the least ugly one that actually +# works.) +ifndef CONTAINER_ENGINE +CONTAINER_ENGINE=$(shell command -v podman 2>/dev/null || echo docker --config=$(CONTAINER_ENGINE_CONFIG_DIR)) +endif + +REGISTRY_USER ?= +REGISTRY_TOKEN ?= + +# TODO: Figure out how to discover this dynamically +OSDE2E_CONVENTION_DIR := boilerplate/openshift/golang-osd-operator-osde2e + +# TODO: figure out how to container-engine-login only once across multiple `make` calls +.PHONY: container-build-push-one +container-build-push-one: container-engine-login + @(if [[ -z "${IMAGE_URI}" ]]; then echo "Must specify IMAGE_URI"; exit 1; fi) + @(if [[ -z "${DOCKERFILE_PATH}" ]]; then echo "Must specify DOCKERFILE_PATH"; exit 1; fi) + ${CONTAINER_ENGINE} build --pull -f $(DOCKERFILE_PATH) -t $(IMAGE_URI) . + ${CONTAINER_ENGINE} push ${IMAGE_URI} + +# log into quay.io +.PHONY: container-engine-login +container-engine-login: + @test "${REGISTRY_USER}" != "" && test "${REGISTRY_TOKEN}" != "" || (echo "REGISTRY_USER and REGISTRY_TOKEN must be defined" && exit 1) + mkdir -p ${CONTAINER_ENGINE_CONFIG_DIR} + @${CONTAINER_ENGINE} login -u="${REGISTRY_USER}" -p="${REGISTRY_TOKEN}" quay.io + +###################### +# Targets used by osde2e test harness +###################### + +# create e2e scaffolding +.PHONY: e2e-harness-generate +e2e-harness-generate: + ${OSDE2E_CONVENTION_DIR}/e2e-harness-generate.sh $(OPERATOR_NAME) $(OSDE2E_CONVENTION_DIR) + +# create binary +.PHONY: e2e-harness-build +e2e-harness-build: GOFLAGS_MOD=-mod=mod +e2e-harness-build: GOENV=GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 GOFLAGS="${GOFLAGS_MOD}" +e2e-harness-build: + go mod tidy + ${GOENV} go test ./osde2e -v -c --tags=integration -o harness.test + +# TODO: Push to a known image tag and commit id +# push harness image +.PHONY: e2e-image-build-push +e2e-image-build-push: + ${OSDE2E_CONVENTION_DIR}/e2e-image-build-push.sh "./osde2e/Dockerfile $(IMAGE_REGISTRY)/$(IMAGE_REPOSITORY)/$(HARNESS_IMAGE_NAME):latest" diff --git a/boilerplate/openshift/golang-osd-operator/Dockerfile.olm-registry b/boilerplate/openshift/golang-osd-operator/Dockerfile.olm-registry new file mode 100644 index 00000000..11ce943a --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator/Dockerfile.olm-registry @@ -0,0 +1,21 @@ +FROM quay.io/openshift/origin-operator-registry:4.12 AS builder +ARG SAAS_OPERATOR_DIR +COPY ${SAAS_OPERATOR_DIR} manifests +RUN initializer --permissive + +FROM registry.access.redhat.com/ubi8/ubi-micro:latest + +COPY --from=builder /bin/registry-server /bin/registry-server +COPY --from=builder /bin/grpc_health_probe /bin/grpc_health_probe +COPY --from=builder /bin/initializer /bin/initializer + +WORKDIR /registry +RUN chgrp -R 0 /registry && chmod -R g+rwx /registry + +USER 1001 + +COPY --from=builder /registry /registry + +EXPOSE 50051 + +CMD ["registry-server", "-t", "/tmp/terminate.log"] diff --git a/boilerplate/openshift/golang-osd-operator/csv-generate/catalog-build.sh b/boilerplate/openshift/golang-osd-operator/csv-generate/catalog-build.sh index 8b5f1d52..904ef767 100755 --- a/boilerplate/openshift/golang-osd-operator/csv-generate/catalog-build.sh +++ b/boilerplate/openshift/golang-osd-operator/csv-generate/catalog-build.sh @@ -36,7 +36,7 @@ check_mandatory_params operator_channel operator_name # Parameters for the Dockerfile SAAS_OPERATOR_DIR="saas-${operator_name}-bundle" BUNDLE_DIR="${SAAS_OPERATOR_DIR}/${operator_name}" -DOCKERFILE_REGISTRY="Dockerfile.olm-registry" +DOCKERFILE_REGISTRY="build/Dockerfile.olm-registry" # Checking SAAS_OPERATOR_DIR exist if [ ! -d "${SAAS_OPERATOR_DIR}/.git" ] ; then @@ -61,31 +61,7 @@ channels: currentCSV: ${operator_name}.v${OPERATOR_NEW_VERSION} EOF -# Build registry -cat < $DOCKERFILE_REGISTRY -FROM quay.io/openshift/origin-operator-registry:4.10.0 AS builder -COPY $SAAS_OPERATOR_DIR manifests -RUN initializer --permissive - -FROM registry.access.redhat.com/ubi8/ubi-micro:8.6-484 - -COPY --from=builder /bin/registry-server /bin/registry-server -COPY --from=builder /bin/grpc_health_probe /bin/grpc_health_probe -COPY --from=builder /bin/initializer /bin/initializer - -WORKDIR /registry -RUN chgrp -R 0 /registry && chmod -R g+rwx /registry - -USER 1001 - -COPY --from=builder /registry /registry - -EXPOSE 50051 - -CMD ["registry-server", "-t", "/tmp/terminate.log"] -EOF - -${CONTAINER_ENGINE} build --pull -f $DOCKERFILE_REGISTRY --tag "${registry_image}:${operator_channel}-latest" . +${CONTAINER_ENGINE} build --pull -f "${DOCKERFILE_REGISTRY}" --build-arg "SAAS_OPERATOR_DIR=${SAAS_OPERATOR_DIR}" --tag "${registry_image}:${operator_channel}-latest" . if [ $? -ne 0 ] ; then echo "docker build failed, exiting..." diff --git a/boilerplate/openshift/golang-osd-operator/csv-generate/csv-generate.sh b/boilerplate/openshift/golang-osd-operator/csv-generate/csv-generate.sh index 5e7a7104..c6563ebf 100755 --- a/boilerplate/openshift/golang-osd-operator/csv-generate/csv-generate.sh +++ b/boilerplate/openshift/golang-osd-operator/csv-generate/csv-generate.sh @@ -48,7 +48,9 @@ fi if [[ -z "$CONTAINER_ENGINE" ]]; then YQ_CMD="yq" else - YQ_CMD="$CONTAINER_ENGINE run --rm -i quay.io/app-sre/yq:3.4.1 yq" + yq_image="quay.io/app-sre/yq:3.4.1" + $CONTAINER_ENGINE pull $yq_image + YQ_CMD="$CONTAINER_ENGINE run --rm -i $yq_image yq" fi # Get the image URI as repo URL + image digest @@ -175,5 +177,5 @@ else else CE_OPTS="-v `pwd`:`pwd`" fi - $CONTAINER_ENGINE run --rm ${CE_OPTS} -u `id -u`:0 -w `pwd` registry.access.redhat.com/ubi8/python-36:1-134 /bin/bash -c "python -m pip install oyaml; python ./boilerplate/openshift/golang-osd-operator/csv-generate/common-generate-operator-bundle.py -o ${operator_name} -d ${OUTPUT_DIR} ${PREV_VERSION_OPTS} -i ${REPO_DIGEST} -V ${operator_version}" + $CONTAINER_ENGINE run --pull=always --rm ${CE_OPTS} -u `id -u`:0 -w `pwd` registry.access.redhat.com/ubi8/python-36 /bin/bash -c "python -m pip install --disable-pip-version-check oyaml; python ./boilerplate/openshift/golang-osd-operator/csv-generate/common-generate-operator-bundle.py -o ${operator_name} -d ${OUTPUT_DIR} ${PREV_VERSION_OPTS} -i ${REPO_DIGEST} -V ${operator_version}" fi diff --git a/boilerplate/openshift/golang-osd-operator/dependabot.yml b/boilerplate/openshift/golang-osd-operator/dependabot.yml index 92e7d171..d4f250ce 100644 --- a/boilerplate/openshift/golang-osd-operator/dependabot.yml +++ b/boilerplate/openshift/golang-osd-operator/dependabot.yml @@ -10,3 +10,5 @@ updates: ignore: - dependency-name: "app-sre/boilerplate" # don't upgrade boilerplate via these means + - dependency-name: "openshift/origin-operator-registry" + # don't upgrade origin-operator-registry via these means diff --git a/boilerplate/openshift/golang-osd-operator/update b/boilerplate/openshift/golang-osd-operator/update index 3126a3e3..da8ce615 100755 --- a/boilerplate/openshift/golang-osd-operator/update +++ b/boilerplate/openshift/golang-osd-operator/update @@ -23,16 +23,30 @@ mkdir -p $REPO_ROOT/.github echo "Copying dependabot.yml to .github/dependabot.yml" cp ${HERE}/dependabot.yml ${REPO_ROOT}/.github/dependabot.yml +# Add olm-registry Dockerfile +mkdir -p $REPO_ROOT/build +echo "Copying Dockerfile.olm-registry to build/Dockerfile.olm-registry" +cp ${HERE}/Dockerfile.olm-registry ${REPO_ROOT}/build/Dockerfile.olm-registry +# if the gitignore file exists, remove the olm-registry line +if [[ -f ${REPO_ROOT}/.gitignore ]]; then + ${SED?} -i "/Dockerfile.olm-registry/d" ${REPO_ROOT}/.gitignore +fi + # Update Dockerfile builder image -DOCKERFILE=build/Dockerfile -echo "Overwriting $DOCKERFILE's initial FROM with $IMAGE_PULL_PATH" -${SED?} -i "1s,.*,FROM $IMAGE_PULL_PATH AS builder," $DOCKERFILE - -# Update any UBI images using latest tags to use a versioned tag that is compatible with dependabot -for ubi_latest in $(grep -oE 'registry.access.redhat.com/ubi[7-9]/ubi.*?:latest' ${DOCKERFILE}); do - replacement_image=$(skopeo inspect --override-os linux --override-arch amd64 docker://${ubi_latest} --format "{{.Name}}:{{.Labels.version}}-{{.Labels.release}}" | ${SED?} 's,\.[0-9]\+$,,') - echo "Overwriting ${DOCKERFILE}'s ${ubi_latest} image to ${replacement_image}" - ${SED?} -i "s,${ubi_latest},${replacement_image}," ${DOCKERFILE} +DOCKERFILES=$(ls -1 $REPO_ROOT/build/Dockerfile*) +for file in $DOCKERFILES; do + # only update boilerplate base on the main file + if [[ $file == *"Dockerfile" ]]; then + echo "Overwriting $file's initial FROM with $IMAGE_PULL_PATH" + ${SED?} -i "1s,.*,FROM $IMAGE_PULL_PATH AS builder," $file + fi + + # Update any UBI images using latest tags to use a versioned tag that is compatible with dependabot + for ubi_latest in $(grep -oE 'registry.access.redhat.com/ubi[7-9]/ubi.*?:latest' ${file}); do + replacement_image=$(skopeo inspect --override-os linux --override-arch amd64 docker://${ubi_latest} --format "{{.Name}}:{{.Labels.version}}-{{.Labels.release}}" | ${SED?} 's,\.[0-9]\+$,,') + echo "Overwriting ${file}'s ${ubi_latest} image to ${replacement_image}" + ${SED?} -i "s,${ubi_latest},${replacement_image}," ${file} + done done # Add ci-operator configuration diff --git a/boilerplate/update.cfg b/boilerplate/update.cfg index 7e194b21..30f4bcee 100644 --- a/boilerplate/update.cfg +++ b/boilerplate/update.cfg @@ -1,2 +1,3 @@ # Use standards for Go-based OSD operators openshift/golang-osd-operator +openshift/golang-osd-operator-osde2e diff --git a/build/Dockerfile b/build/Dockerfile index 3eaffa64..7117ae98 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/app-sre/boilerplate:image-v3.0.2 AS builder +FROM quay.io/app-sre/boilerplate:image-v3.0.3 AS builder RUN mkdir -p /workdir COPY . /workdir diff --git a/build/Dockerfile.olm-registry b/build/Dockerfile.olm-registry new file mode 100644 index 00000000..e9a54a6e --- /dev/null +++ b/build/Dockerfile.olm-registry @@ -0,0 +1,21 @@ +FROM quay.io/openshift/origin-operator-registry:4.12 AS builder +ARG SAAS_OPERATOR_DIR +COPY ${SAAS_OPERATOR_DIR} manifests +RUN initializer --permissive + +FROM registry.access.redhat.com/ubi8/ubi-micro:8.7-6 + +COPY --from=builder /bin/registry-server /bin/registry-server +COPY --from=builder /bin/grpc_health_probe /bin/grpc_health_probe +COPY --from=builder /bin/initializer /bin/initializer + +WORKDIR /registry +RUN chgrp -R 0 /registry && chmod -R g+rwx /registry + +USER 1001 + +COPY --from=builder /registry /registry + +EXPOSE 50051 + +CMD ["registry-server", "-t", "/tmp/terminate.log"] diff --git a/go.mod b/go.mod index cc641b76..613e2a6b 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,8 @@ require ( k8s.io/api v0.26.3 k8s.io/apimachinery v0.26.3 k8s.io/client-go v0.26.3 - sigs.k8s.io/controller-runtime v0.14.0 + sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/e2e-framework v0.2.0 ) require ( @@ -79,6 +80,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -138,3 +140,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) + +replace github.com/openshift/api => github.com/openshift/api v0.0.0-20230327113148-36ce464529eb diff --git a/go.sum b/go.sum index b227b183..f509ba82 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,7 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/O github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -109,6 +110,7 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -237,6 +239,7 @@ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -303,6 +306,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= @@ -329,8 +334,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= -github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/openshift/api v0.0.0-20230327113148-36ce464529eb h1:nJp2JVf8FsNUbdZ1QjLgmWCdvqRaLNE8dYb5WFQJ3jA= +github.com/openshift/api v0.0.0-20230327113148-36ce464529eb/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -405,6 +410,7 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -848,8 +854,10 @@ oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.14.0 h1:ju2xsov5Ara6FoQuddg+az+rAxsUsTYn2IYyEKCTyDc= -sigs.k8s.io/controller-runtime v0.14.0/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= +sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= +sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/e2e-framework v0.2.0 h1:gD6AWWAHFcHibI69E9TgkNFhh0mVwWtRCHy2RU057jQ= +sigs.k8s.io/e2e-framework v0.2.0/go.mod h1:E6JXj/V4PIlb95jsn2WrNKG+Shb45xaaI7C0+BH4PL8= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= diff --git a/osde2e/Dockerfile b/osde2e/Dockerfile new file mode 100644 index 00000000..0be6c71c --- /dev/null +++ b/osde2e/Dockerfile @@ -0,0 +1,13 @@ + +# THIS FILE IS GENERATED BY BOILERPLATE. DO NOT EDIT. +FROM registry.ci.openshift.org/openshift/release:golang-1.18 AS builder + +ENV PKG=/go/src/github.com/openshift/deployment-validation-operator/ +WORKDIR ${PKG} +COPY . . + +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest + +COPY ./harness.test harness.test + +ENTRYPOINT [ "/harness.test" ] diff --git a/osde2e/deployment_validation_operator_runner_test.go b/osde2e/deployment_validation_operator_runner_test.go new file mode 100644 index 00000000..804a496c --- /dev/null +++ b/osde2e/deployment_validation_operator_runner_test.go @@ -0,0 +1,28 @@ +// THIS FILE IS GENERATED BY BOILERPLATE. DO NOT EDIT. +//go:build integration +// +build integration + +package osde2etests + +import ( + "path/filepath" + "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +const ( + testResultsDirectory = "/test-run-results" + jUnitOutputFilename = "junit-example-addon.xml" +) + +// Test entrypoint. osde2e runs this as a test suite on test pod. +func TestDeploymentValidationOperator(t *testing.T) { + RegisterFailHandler(Fail) + + suiteConfig, reporterConfig := GinkgoConfiguration() + reporterConfig.JUnitReport = filepath.Join(testResultsDirectory, jUnitOutputFilename) + RunSpecs(t, "Deployment Validation Operator", suiteConfig, reporterConfig) + +} + diff --git a/osde2e/deployment_validation_operator_tests.go b/osde2e/deployment_validation_operator_tests.go new file mode 100644 index 00000000..9442f6ea --- /dev/null +++ b/osde2e/deployment_validation_operator_tests.go @@ -0,0 +1,131 @@ +package osde2etests + +import ( + "context" + "fmt" + "time" + + "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" //nolint:golint + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apimachinerylabels "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/e2e-framework/klient/k8s/resources" + "sigs.k8s.io/e2e-framework/klient/wait" + "sigs.k8s.io/e2e-framework/klient/wait/conditions" +) + +var _ = ginkgo.Describe("DVO", ginkgo.Ordered, func() { + const ( + namespaceName = "openshift-deployment-validation-operator" + deploymentName = "deployment-validation-operator" + ) + + var k8s *resources.Resources + + ginkgo.BeforeAll(func() { + // setup the k8s client + cfg, err := config.GetConfig() + Expect(err).Should(BeNil(), "failed to get kubeconfig") + k8s, err = resources.New(cfg) + Expect(err).Should(BeNil(), "resources.New error") + }) + + ginkgo.It("exists and is running", func(ctx context.Context) { + const serviceName = "deployment-validation-operator-metrics" + clusterRoles := []string{ + "deployment-validation-operator-og-admin", + "deployment-validation-operator-og-edit", + "deployment-validation-operator-og-view", + } + + err := k8s.Get(ctx, namespaceName, namespaceName, &v1.Namespace{}) + Expect(err).Should(BeNil(), "unable to get namespace %s", namespaceName) + + err = k8s.Get(ctx, serviceName, namespaceName, &v1.Service{}) + Expect(err).Should(BeNil(), "unable to get service %s", serviceName) + + for _, clusterRoleName := range clusterRoles { + err = k8s.Get(ctx, clusterRoleName, "", &rbacv1.ClusterRole{}) + Expect(err).Should(BeNil(), "unable to get clusterrole %s", clusterRoleName) + } + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{Name: deploymentName, Namespace: namespaceName}, + } + deploymentAvailable := conditions.New(k8s). + DeploymentConditionMatch(deployment, appsv1.DeploymentAvailable, v1.ConditionTrue) + err = wait.For(deploymentAvailable, wait.WithTimeout(10*time.Second)) + Expect(err).Should(BeNil(), "deployment %s never became available", deploymentName) + }) + + ginkgo.Context("validates", func() { + var namespace *v1.Namespace + + ginkgo.BeforeEach(func(ctx context.Context) { + namespace = &v1.Namespace{ObjectMeta: metav1.ObjectMeta{GenerateName: "osde2e-dvo-"}} + err := k8s.Create(ctx, namespace) + Expect(err).Should(BeNil(), "unable to create test namespace %s", namespace.GetName()) + + labels := map[string]string{"app": "test"} + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "osde2e-", Namespace: namespace.GetName()}, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{Labels: labels}, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + {Name: "pause", Image: "registry.k8s.io/pause:latest"}, + }, + }, + }, + }, + } + err = k8s.Create(ctx, deployment) + Expect(err).Should(BeNil(), "unable to create deployment %s", deployment.GetName()) + deploymentAvailable := conditions.New(k8s). + DeploymentConditionMatch(deployment, appsv1.DeploymentAvailable, v1.ConditionTrue) + err = wait.For(deploymentAvailable) + Expect(err).Should(BeNil(), "deployment %s never became available", deployment.GetName()) + + ginkgo.DeferCleanup(k8s.Delete, namespace) + }) + + ginkgo.It("new deployments", func(ctx context.Context) { + //nolint:lll + validationMsg := fmt.Sprintf("\"msg\":\"Set memory requests and limits for your container based on its requirements. Refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits for details.\",\"request.namespace\":%q", namespace.GetName()) + + // Wait for the deployment logs to contain the validation message + Eventually(ctx, func(ctx context.Context) (string, error) { + pods := &v1.PodList{} + lbls := apimachinerylabels.FormatLabels(map[string]string{"app": deploymentName}) + err := k8s.List(ctx, pods, resources.WithLabelSelector(lbls)) + if err != nil { + return "", err + } + if len(pods.Items) < 1 { + return "", fmt.Errorf("unable to find pod for deployment %s", deploymentName) + } + clientset, err := kubernetes.NewForConfig(k8s.GetConfig()) + if err != nil { + return "", err + } + req := clientset.CoreV1().Pods(namespaceName). + GetLogs(pods.Items[0].GetName(), &v1.PodLogOptions{}) + logs, err := req.DoRaw(ctx) + if err != nil { + return "", err + } + return string(logs), nil + }). + WithPolling(30 * time.Second). + WithTimeout(10 * time.Minute). + Should(ContainSubstring(validationMsg)) + }) + }) +})