From 6e08e9a3d7d36b754064b3df36eea58ca81c49f1 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Fri, 4 Aug 2023 16:55:31 -0400 Subject: [PATCH 1/3] initial testing changes for passing e2e Signed-off-by: Bryce Palmer --- Makefile | 34 +- go.mod | 3 +- .../ansible/advanced_molecule.go | 238 +++++-------- .../{internal => }/ansible/constants.go | 0 hack/generate/samples/ansible/generate.go | 243 +++++++++++++ hack/generate/samples/ansible/memcached.go | 75 ++++ .../samples/ansible/memcached_molecule.go | 128 +++++++ .../ansible/testdata/ansible.cfg | 0 .../testdata/fixture_collection/galaxy.yml | 0 .../roles/dummy/tasks/main.yml | 0 .../testdata/inventory/group_vars/test.yml | 0 .../ansible/testdata/inventory/hosts | 0 .../finalizerconcurrencyfinalizer.yml | 0 .../ansible/testdata/plugins/filter/test.py | 0 .../ansible/testdata/secret.yml | 0 .../ansible/testdata/tasks/argstest_test.yml | 0 .../ansible/testdata/tasks/casetest_test.yml | 0 .../tasks/clusterannotationtest_test.yml | 0 .../testdata/tasks/collectiontest_test.yml | 0 .../tasks/finalizerconcurrencytest_test.yml | 0 .../testdata/tasks/inventorytest_test.yml | 0 .../tasks/reconciliationtest_test.yml | 0 .../testdata/tasks/secretstest_test.yml | 0 .../testdata/tasks/selectortest_test.yml | 0 .../testdata/tasks/subresourcestest_test.yml | 0 .../ansible/testdata/watches.yaml | 0 hack/generate/samples/generate_testdata.go | 4 +- .../samples/internal/ansible/generate.go | 23 -- .../samples/internal/ansible/memcached.go | 136 -------- .../internal/ansible/memcached_molecule.go | 210 ----------- hack/generate/samples/molecule/generate.go | 6 +- internal/version/version.go | 4 +- pkg/testutils/command/command.go | 106 ++++++ pkg/testutils/e2e/certmanager/helpers.go | 85 +++++ pkg/testutils/e2e/helpers.go | 52 +++ pkg/testutils/e2e/kind/helpers.go | 35 ++ pkg/testutils/e2e/metrics/helpers.go | 101 ++++++ pkg/testutils/e2e/olm/helpers.go | 173 +++++++++ pkg/testutils/e2e/operator/helpers.go | 99 ++++++ pkg/testutils/e2e/prometheus/helpers.go | 71 ++++ pkg/testutils/kubernetes/kubectl.go | 173 +++++++++ pkg/testutils/kubernetes/version.go | 118 +++++++ pkg/testutils/sample/cli/cli.go | 330 ++++++++++++++++++ pkg/testutils/sample/generator.go | 145 ++++++++ pkg/testutils/sample/sample.go | 267 ++++++++++++++ test/common/scorecard.go | 122 ------- test/e2e/ansible/cluster_test.go | 223 ++++-------- test/e2e/ansible/local_test.go | 19 +- test/e2e/ansible/olm_test.go | 58 --- test/e2e/ansible/scorecard_test.go | 22 -- test/e2e/ansible/suite_test.go | 240 +++++++------ testdata/ansible/memcached-operator/Makefile | 231 ------------ testdata/ansible/memcached-operator/PROJECT | 20 -- .../memcached-operator/bundle.Dockerfile | 17 - ...nitoring.coreos.com_v1_servicemonitor.yaml | 23 -- ...er-manager-metrics-service_v1_service.yaml | 23 -- ...c.authorization.k8s.io_v1_clusterrole.yaml | 17 - ...cached-operator.clusterserviceversion.yaml | 261 -------------- .../bundle/metadata/annotations.yaml | 11 - .../bundle/tests/scorecard/config.yaml | 70 ---- .../config/manager/kustomization.yaml | 8 - ...cached-operator.clusterserviceversion.yaml | 42 --- .../config/manifests/kustomization.yaml | 7 - .../memcached-operator/config/rbac/role.yaml | 57 --- .../config/scorecard/bases/config.yaml | 7 - .../config/scorecard/kustomization.yaml | 16 - .../scorecard/patches/basic.config.yaml | 10 - .../config/scorecard/patches/olm.config.yaml | 50 --- .../ansible/memcached-operator/watches.yaml | 7 - .../.gitignore | 0 .../Dockerfile | 2 +- testdata/memcached-molecule-operator/Makefile | 109 ++++++ testdata/memcached-molecule-operator/PROJECT | 39 +++ .../crd/bases/cache.example.com_foos.yaml | 44 +++ .../bases/cache.example.com_memcacheds.yaml | 0 .../crd/bases/cache.example.com_memfins.yaml} | 24 +- .../crd/bases/ignore.example.com_secrets.yaml | 44 +++ .../config/crd/kustomization.yaml | 3 + .../config/default/kustomization.yaml | 4 +- .../default/manager_auth_proxy_patch.yaml | 2 +- .../config/default/manager_config_patch.yaml | 0 .../config/manager/kustomization.yaml | 2 + .../config/manager/manager.yaml | 19 +- .../config/prometheus/kustomization.yaml | 0 .../config/prometheus/monitor.yaml | 4 +- .../rbac/auth_proxy_client_clusterrole.yaml | 4 +- .../config/rbac/auth_proxy_role.yaml | 4 +- .../config/rbac/auth_proxy_role_binding.yaml | 4 +- .../config/rbac/auth_proxy_service.yaml | 4 +- .../config/rbac/foo_editor_role.yaml | 31 ++ .../config/rbac/foo_viewer_role.yaml | 27 ++ .../rbac/ignore_secret_editor_role.yaml | 31 ++ .../rbac/ignore_secret_viewer_role.yaml | 27 ++ .../config/rbac/kustomization.yaml | 0 .../config/rbac/leader_election_role.yaml | 4 +- .../rbac/leader_election_role_binding.yaml | 4 +- .../config/rbac/memcached_editor_role.yaml | 4 +- .../config/rbac/memcached_viewer_role.yaml | 4 +- .../config/rbac/memfin_editor_role.yaml | 31 ++ .../config/rbac/memfin_viewer_role.yaml | 27 ++ .../config/rbac/role.yaml | 126 +++++++ .../config/rbac/role_binding.yaml | 4 +- .../config/rbac/service_account.yaml | 4 +- .../config/samples/cache_v1alpha1_foo.yaml | 12 + .../samples/cache_v1alpha1_memcached.yaml | 4 +- .../config/samples/cache_v1alpha1_memfin.yaml | 12 + .../config/samples/ignore_v1_secret.yaml | 12 + .../config/samples/kustomization.yaml | 3 + .../config/testing/debug_logs_patch.yaml | 0 .../config/testing/kustomization.yaml | 1 + .../config/testing/manager_image.yaml | 0 .../config/testing/pull_policy/Always.yaml | 0 .../testing/pull_policy/IfNotPresent.yaml | 0 .../config/testing/pull_policy/Never.yaml | 0 .../config/testing/watch_namespace_patch.yaml | 16 + .../molecule/default/converge.yml | 0 .../molecule/default/create.yml | 0 .../molecule/default/destroy.yml | 0 .../molecule/default/kustomize.yml | 0 .../molecule/default/molecule.yml | 0 .../molecule/default/prepare.yml | 0 .../molecule/default/tasks/foo_test.yml | 13 + .../molecule/default/tasks/memcached_test.yml | 75 +++- .../molecule/default/tasks/memfin_test.yml | 13 + .../molecule/default/verify.yml | 0 .../molecule/kind/converge.yml | 0 .../molecule/kind/create.yml | 0 .../molecule/kind/destroy.yml | 0 .../molecule/kind/molecule.yml | 0 .../playbooks/.placeholder | 0 .../playbooks/foo.yml | 9 + .../playbooks/memcached.yml | 0 .../playbooks/memfin.yml | 9 + .../requirements.yml | 0 .../roles/.placeholder | 0 .../roles/foo}/README.md | 0 .../roles/foo/defaults/main.yml | 2 + .../roles/foo}/files/.placeholder | 0 .../roles/foo/handlers/main.yml | 2 + .../roles/foo}/meta/main.yml | 0 .../roles/foo/tasks/main.yml | 2 + .../roles/foo}/templates/.placeholder | 0 .../roles/foo/vars/main.yml | 2 + .../roles/memcached/README.md | 43 +++ .../roles/memcached/defaults/main.yml | 0 .../roles/memcached/files/.placeholder | 0 .../roles/memcached/handlers/main.yml | 0 .../roles/memcached/meta/main.yml | 64 ++++ .../roles/memcached/tasks/main.yml | 41 +++ .../roles/memcached/templates/.placeholder | 0 .../roles/memcached/vars/main.yml | 0 .../roles/memfin/README.md | 43 +++ .../roles/memfin/defaults/main.yml | 2 + .../roles/memfin/files/.placeholder | 0 .../roles/memfin/handlers/main.yml | 2 + .../roles/memfin/meta/main.yml | 64 ++++ .../roles/memfin/tasks/main.yml | 8 + .../roles/memfin/templates/.placeholder | 0 .../roles/memfin/vars/main.yml | 2 + .../roles/secret/README.md | 43 +++ .../roles/secret/defaults/main.yml | 2 + .../roles/secret/files/.placeholder | 0 .../roles/secret/handlers/main.yml | 2 + .../roles/secret/meta/main.yml | 64 ++++ .../roles/secret/tasks/main.yml | 15 + .../roles/secret/templates/.placeholder | 0 .../roles/secret/vars/main.yml | 2 + .../memcached-molecule-operator/watches.yaml | 27 ++ 168 files changed, 3707 insertions(+), 1960 deletions(-) rename hack/generate/samples/{internal => }/ansible/advanced_molecule.go (62%) rename hack/generate/samples/{internal => }/ansible/constants.go (100%) create mode 100644 hack/generate/samples/ansible/generate.go create mode 100644 hack/generate/samples/ansible/memcached.go create mode 100644 hack/generate/samples/ansible/memcached_molecule.go rename hack/generate/samples/{internal => }/ansible/testdata/ansible.cfg (100%) rename hack/generate/samples/{internal => }/ansible/testdata/fixture_collection/galaxy.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/fixture_collection/roles/dummy/tasks/main.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/inventory/group_vars/test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/inventory/hosts (100%) rename hack/generate/samples/{internal => }/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/plugins/filter/test.py (100%) rename hack/generate/samples/{internal => }/ansible/testdata/secret.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/argstest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/casetest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/clusterannotationtest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/collectiontest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/finalizerconcurrencytest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/inventorytest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/reconciliationtest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/secretstest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/selectortest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/tasks/subresourcestest_test.yml (100%) rename hack/generate/samples/{internal => }/ansible/testdata/watches.yaml (100%) delete mode 100644 hack/generate/samples/internal/ansible/generate.go delete mode 100644 hack/generate/samples/internal/ansible/memcached.go delete mode 100644 hack/generate/samples/internal/ansible/memcached_molecule.go create mode 100644 pkg/testutils/command/command.go create mode 100644 pkg/testutils/e2e/certmanager/helpers.go create mode 100644 pkg/testutils/e2e/helpers.go create mode 100644 pkg/testutils/e2e/kind/helpers.go create mode 100644 pkg/testutils/e2e/metrics/helpers.go create mode 100644 pkg/testutils/e2e/olm/helpers.go create mode 100644 pkg/testutils/e2e/operator/helpers.go create mode 100644 pkg/testutils/e2e/prometheus/helpers.go create mode 100644 pkg/testutils/kubernetes/kubectl.go create mode 100644 pkg/testutils/kubernetes/version.go create mode 100644 pkg/testutils/sample/cli/cli.go create mode 100644 pkg/testutils/sample/generator.go create mode 100644 pkg/testutils/sample/sample.go delete mode 100644 test/common/scorecard.go delete mode 100644 test/e2e/ansible/olm_test.go delete mode 100644 test/e2e/ansible/scorecard_test.go delete mode 100644 testdata/ansible/memcached-operator/Makefile delete mode 100644 testdata/ansible/memcached-operator/PROJECT delete mode 100644 testdata/ansible/memcached-operator/bundle.Dockerfile delete mode 100644 testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-monitor_monitoring.coreos.com_v1_servicemonitor.yaml delete mode 100644 testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml delete mode 100644 testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml delete mode 100644 testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml delete mode 100644 testdata/ansible/memcached-operator/bundle/metadata/annotations.yaml delete mode 100644 testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml delete mode 100644 testdata/ansible/memcached-operator/config/manager/kustomization.yaml delete mode 100644 testdata/ansible/memcached-operator/config/manifests/bases/memcached-operator.clusterserviceversion.yaml delete mode 100644 testdata/ansible/memcached-operator/config/manifests/kustomization.yaml delete mode 100644 testdata/ansible/memcached-operator/config/rbac/role.yaml delete mode 100644 testdata/ansible/memcached-operator/config/scorecard/bases/config.yaml delete mode 100644 testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml delete mode 100644 testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml delete mode 100644 testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml delete mode 100644 testdata/ansible/memcached-operator/watches.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/.gitignore (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/Dockerfile (81%) create mode 100644 testdata/memcached-molecule-operator/Makefile create mode 100644 testdata/memcached-molecule-operator/PROJECT create mode 100644 testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_foos.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/crd/bases/cache.example.com_memcacheds.yaml (100%) rename testdata/{ansible/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml => memcached-molecule-operator/config/crd/bases/cache.example.com_memfins.yaml} (77%) create mode 100644 testdata/memcached-molecule-operator/config/crd/bases/ignore.example.com_secrets.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/crd/kustomization.yaml (71%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/default/kustomization.yaml (89%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/default/manager_auth_proxy_patch.yaml (96%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/default/manager_config_patch.yaml (100%) create mode 100644 testdata/memcached-molecule-operator/config/manager/kustomization.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/manager/manager.yaml (76%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/prometheus/kustomization.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/prometheus/monitor.yaml (85%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/auth_proxy_client_clusterrole.yaml (73%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/auth_proxy_role.yaml (79%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/auth_proxy_role_binding.yaml (79%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/auth_proxy_service.yaml (79%) create mode 100644 testdata/memcached-molecule-operator/config/rbac/foo_editor_role.yaml create mode 100644 testdata/memcached-molecule-operator/config/rbac/foo_viewer_role.yaml create mode 100644 testdata/memcached-molecule-operator/config/rbac/ignore_secret_editor_role.yaml create mode 100644 testdata/memcached-molecule-operator/config/rbac/ignore_secret_viewer_role.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/kustomization.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/leader_election_role.yaml (84%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/leader_election_role_binding.yaml (79%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/memcached_editor_role.yaml (82%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/memcached_viewer_role.yaml (81%) create mode 100644 testdata/memcached-molecule-operator/config/rbac/memfin_editor_role.yaml create mode 100644 testdata/memcached-molecule-operator/config/rbac/memfin_viewer_role.yaml create mode 100644 testdata/memcached-molecule-operator/config/rbac/role.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/role_binding.yaml (79%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/rbac/service_account.yaml (69%) create mode 100644 testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_foo.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/samples/cache_v1alpha1_memcached.yaml (67%) create mode 100644 testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memfin.yaml create mode 100644 testdata/memcached-molecule-operator/config/samples/ignore_v1_secret.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/samples/kustomization.yaml (62%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/debug_logs_patch.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/kustomization.yaml (93%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/manager_image.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/pull_policy/Always.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/pull_policy/IfNotPresent.yaml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/config/testing/pull_policy/Never.yaml (100%) create mode 100644 testdata/memcached-molecule-operator/config/testing/watch_namespace_patch.yaml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/converge.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/create.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/destroy.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/kustomize.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/molecule.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/prepare.yml (100%) create mode 100644 testdata/memcached-molecule-operator/molecule/default/tasks/foo_test.yml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/tasks/memcached_test.yml (58%) create mode 100644 testdata/memcached-molecule-operator/molecule/default/tasks/memfin_test.yml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/default/verify.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/kind/converge.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/kind/create.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/kind/destroy.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/molecule/kind/molecule.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/playbooks/.placeholder (100%) create mode 100644 testdata/memcached-molecule-operator/playbooks/foo.yml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/playbooks/memcached.yml (100%) create mode 100644 testdata/memcached-molecule-operator/playbooks/memfin.yml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/requirements.yml (100%) rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/roles/.placeholder (100%) rename testdata/{ansible/memcached-operator/roles/memcached => memcached-molecule-operator/roles/foo}/README.md (100%) create mode 100644 testdata/memcached-molecule-operator/roles/foo/defaults/main.yml rename testdata/{ansible/memcached-operator/roles/memcached => memcached-molecule-operator/roles/foo}/files/.placeholder (100%) create mode 100644 testdata/memcached-molecule-operator/roles/foo/handlers/main.yml rename testdata/{ansible/memcached-operator/roles/memcached => memcached-molecule-operator/roles/foo}/meta/main.yml (100%) create mode 100644 testdata/memcached-molecule-operator/roles/foo/tasks/main.yml rename testdata/{ansible/memcached-operator/roles/memcached => memcached-molecule-operator/roles/foo}/templates/.placeholder (100%) create mode 100644 testdata/memcached-molecule-operator/roles/foo/vars/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/memcached/README.md rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/roles/memcached/defaults/main.yml (100%) create mode 100644 testdata/memcached-molecule-operator/roles/memcached/files/.placeholder rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/roles/memcached/handlers/main.yml (100%) create mode 100644 testdata/memcached-molecule-operator/roles/memcached/meta/main.yml rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/roles/memcached/tasks/main.yml (79%) create mode 100644 testdata/memcached-molecule-operator/roles/memcached/templates/.placeholder rename testdata/{ansible/memcached-operator => memcached-molecule-operator}/roles/memcached/vars/main.yml (100%) create mode 100644 testdata/memcached-molecule-operator/roles/memfin/README.md create mode 100644 testdata/memcached-molecule-operator/roles/memfin/defaults/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/memfin/files/.placeholder create mode 100644 testdata/memcached-molecule-operator/roles/memfin/handlers/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/memfin/meta/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/memfin/tasks/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/memfin/templates/.placeholder create mode 100644 testdata/memcached-molecule-operator/roles/memfin/vars/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/secret/README.md create mode 100644 testdata/memcached-molecule-operator/roles/secret/defaults/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/secret/files/.placeholder create mode 100644 testdata/memcached-molecule-operator/roles/secret/handlers/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/secret/meta/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/secret/tasks/main.yml create mode 100644 testdata/memcached-molecule-operator/roles/secret/templates/.placeholder create mode 100644 testdata/memcached-molecule-operator/roles/secret/vars/main.yml create mode 100644 testdata/memcached-molecule-operator/watches.yaml diff --git a/Makefile b/Makefile index c49004b..2ec914b 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,11 @@ SHELL = /bin/bash # version is moved to a separate repo and release process. export IMAGE_VERSION = v1.31.0 # Build-time variables to inject into binaries -export SIMPLE_VERSION = $(shell (test "$(shell git describe --tags)" = "$(shell git describe --tags --abbrev=0)" && echo $(shell git describe --tags)) || echo $(shell git describe --tags --abbrev=0)+git) -export GIT_VERSION = $(shell git describe --dirty --tags --always) -export GIT_COMMIT = $(shell git rev-parse HEAD) +# export SIMPLE_VERSION = $(shell (test "$(shell git describe --tags)" = "$(shell git describe --tags --abbrev=0)" && echo $(shell git describe --tags)) || echo $(shell git describe --tags --abbrev=0)+git) +# export GIT_VERSION = $(shell git describe --dirty --tags --always) +# export GIT_COMMIT = $(shell git rev-parse HEAD) export K8S_VERSION = 1.26.0 -export OPERATOR_SDK_VERSION = $(IMAGE_VERSION) - # Build settings export TOOLS_DIR = tools/bin export SCRIPTS_DIR = tools/scripts @@ -37,9 +35,9 @@ export PATH := $(PWD)/$(BUILD_DIR):$(PWD)/$(TOOLS_DIR):$(PATH) ##@ Development .PHONY: generate -generate: operator-sdk build # Generate CLI docs and samples +generate: build # Generate CLI docs and samples rm -rf testdata - go run ./hack/generate/samples/generate_testdata.go --bin $(OPERATOR_SDK) + go run ./hack/generate/samples/generate_testdata.go go generate ./... .PHONY: fix @@ -142,11 +140,12 @@ e2e_targets := test-e2e $(e2e_tests) export KIND_CLUSTER := osdk-test KUBEBUILDER_ASSETS = $(PWD)/$(shell go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest && $(shell go env GOPATH)/bin/setup-envtest use $(K8S_VERSION) --bin-dir tools/bin/ -p path) -test-e2e-setup:: operator-sdk build dev-install cluster-create +test-e2e-setup:: build dev-install cluster-create .PHONY: cluster-create cluster-create:: [[ "`$(TOOLS_DIR)/kind get clusters`" =~ "$(KIND_CLUSTER)" ]] || $(TOOLS_DIR)/kind create cluster --image="kindest/node:v$(K8S_VERSION)" --name $(KIND_CLUSTER) + $(TOOLS_DIR)/kind export kubeconfig --name $(KIND_CLUSTER) .PHONY: dev-install dev-install:: @@ -164,29 +163,12 @@ test-e2e-teardown: $(e2e_targets):: test-e2e-setup test-e2e:: $(e2e_tests) ## Run e2e tests -test-e2e-ansible:: operator-sdk image/ansible-operator ## Run Ansible e2e tests - go test -count=1 ./internal/ansible/proxy/... +test-e2e-ansible:: image/ansible-operator ## Run Ansible e2e tests go test ./test/e2e/ansible -v -ginkgo.v test-e2e-ansible-molecule:: install dev-install image/ansible-operator ## Run molecule-based Ansible e2e tests go run ./hack/generate/samples/molecule/generate.go ./hack/tests/e2e-ansible-molecule.sh -## Location to install dependencies to -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) - -.PHONY: operator-sdk -OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk -operator-sdk: ## Download operator-sdk locally if necessary. - @{ \ - set -e ;\ - mkdir -p $(dir $(OPERATOR_SDK)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ - chmod +x $(OPERATOR_SDK) ;\ - } - .DEFAULT_GOAL := help .PHONY: help help: ## Show this help screen. diff --git a/go.mod b/go.mod index 8650571..d02602b 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 github.com/onsi/ginkgo/v2 v2.7.0 github.com/onsi/gomega v1.24.2 - github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 github.com/operator-framework/operator-lib v0.11.1-0.20230306195046-28cadc6b6055 github.com/operator-framework/operator-registry v1.28.0 github.com/prometheus/client_golang v1.14.0 @@ -31,6 +30,8 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +require github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 // indirect + require ( github.com/Microsoft/hcsshim v0.9.4 // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect diff --git a/hack/generate/samples/internal/ansible/advanced_molecule.go b/hack/generate/samples/ansible/advanced_molecule.go similarity index 62% rename from hack/generate/samples/internal/ansible/advanced_molecule.go rename to hack/generate/samples/ansible/advanced_molecule.go index 693b85b..ae77ce0 100644 --- a/hack/generate/samples/internal/ansible/advanced_molecule.go +++ b/hack/generate/samples/ansible/advanced_molecule.go @@ -21,69 +21,22 @@ import ( "strings" log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime/schema" kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" ) -// AdvancedMolecule defines the context for the sample -type AdvancedMolecule struct { - ctx *pkg.SampleContext -} - -// GenerateAdvancedMoleculeSample will call all actions to create the directory and generate the sample -// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT -// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files -// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext. -func GenerateAdvancedMoleculeSample(binaryPath, samplesPath string) { - ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "advanced-molecule-operator"), - "GO111MODULE=on") - pkg.CheckError("generating Ansible Molecule Advanced Operator context", err) - - molecule := AdvancedMolecule{&ctx} - molecule.Prepare() - molecule.Run() -} - -// Prepare the Context for the Memcached Ansible Sample -// Note that sample directory will be re-created and the context data for the sample -// will be set such as the domain and GVK. -func (ma *AdvancedMolecule) Prepare() { - log.Infof("destroying directory for memcached Ansible samples") - ma.ctx.Destroy() - - log.Infof("creating directory") - err := ma.ctx.Prepare() - pkg.CheckError("creating directory for Advanced Molecule Sample", err) - - log.Infof("setting domain and GVK") - // nolint:goconst - ma.ctx.Domain = "example.com" - // nolint:goconst - ma.ctx.Version = "v1alpha1" - ma.ctx.Group = "test" - ma.ctx.Kind = "InventoryTest" -} - -// Run the steps to create the Memcached Ansible Sample -func (ma *AdvancedMolecule) Run() { - log.Infof("creating the project") - err := ma.ctx.Init( - "--plugins", "ansible", - "--group", ma.ctx.Group, - "--version", ma.ctx.Version, - "--kind", ma.ctx.Kind, - "--domain", ma.ctx.Domain, - "--generate-role", - "--generate-playbook") - pkg.CheckError("creating the project", err) - - log.Infof("enabling multigroup support") - err = ma.ctx.AllowProjectBeMultiGroup() +func ImplementAdvancedMolecule(sample sample.Sample, image string) { + log.Info("enabling multigroup support") + err := e2e.AllowProjectBeMultiGroup(sample) pkg.CheckError("updating PROJECT file", err) - inventoryRoleTask := filepath.Join(ma.ctx.Dir, "roles", "inventorytest", "tasks", "main.yml") - log.Infof("inserting code to inventory role task") + inventoryRoleTask := filepath.Join(sample.Dir(), "roles", "inventorytest", "tasks", "main.yml") + log.Info("inserting code to inventory role task") const inventoryRoleTaskFragment = ` - when: sentinel | test block: @@ -103,29 +56,29 @@ func (ma *AdvancedMolecule) Run() { inventoryRoleTaskFragment) pkg.CheckError("replacing inventory task", err) - log.Infof("updating inventorytest sample") + log.Info("updating inventorytest sample") err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "samples", "test_v1alpha1_inventorytest.yaml"), + filepath.Join(sample.Dir(), "config", "samples", "test_v1alpha1_inventorytest.yaml"), "name: inventorytest-sample", inventorysampleFragment) pkg.CheckError("updating inventorytest sample", err) - log.Infof("updating spec of inventorytest sample") + log.Info("updating spec of inventorytest sample") err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "samples", "test_v1alpha1_inventorytest.yaml"), + filepath.Join(sample.Dir(), "config", "samples", "test_v1alpha1_inventorytest.yaml"), "# TODO(user): Add fields here", "size: 3") pkg.CheckError("updating spec of inventorytest sample", err) - ma.addPlaybooks() - ma.updatePlaybooks() - ma.addMocksFromTestdata() - ma.updateDockerfile() - ma.updateConfig() + removeFixmeFromPlaybooks(sample.Dir(), sample.GVKs()) + updatePlaybooks(sample.Dir()) + addMocksFromTestdata(sample.Dir(), sample.CommandContext()) + updateDockerfile(sample.Dir()) + updateConfig(sample.Dir()) } -func (ma *AdvancedMolecule) updateConfig() { - log.Infof("adding customized roles") +func updateConfig(dir string) { + log.Info("adding customized roles") const cmRolesFragment = ` ## ## Base operator rules ## @@ -156,101 +109,103 @@ func (ma *AdvancedMolecule) updateConfig() { - watch #+kubebuilder:scaffold:rules` err := kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "rbac", "role.yaml"), + filepath.Join(dir, "config", "rbac", "role.yaml"), "#+kubebuilder:scaffold:rules", cmRolesFragment) pkg.CheckError("adding customized roles", err) - log.Infof("adding manager arg") + log.Info("adding manager arg") const ansibleVaultArg = ` - --ansible-args='--vault-password-file /opt/ansible/pwd.yml'` err = kbutil.InsertCode( - filepath.Join(ma.ctx.Dir, "config", "manager", "manager.yaml"), + filepath.Join(dir, "config", "manager", "manager.yaml"), "- --leader-election-id=advanced-molecule-operator", ansibleVaultArg) pkg.CheckError("adding manager arg", err) - log.Infof("adding manager env") + log.Info("adding manager env") const managerEnv = ` - name: ANSIBLE_DEBUG_LOGS value: "TRUE" - name: ANSIBLE_INVENTORY value: /opt/ansible/inventory` err = kbutil.InsertCode( - filepath.Join(ma.ctx.Dir, "config", "manager", "manager.yaml"), + filepath.Join(dir, "config", "manager", "manager.yaml"), "value: explicit", managerEnv) pkg.CheckError("adding manager env", err) - log.Infof("adding vaulting args to the proxy auth") + log.Info("adding vaulting args to the proxy auth") const managerAuthArgs = ` - "--ansible-args='--vault-password-file /opt/ansible/pwd.yml'"` err = kbutil.InsertCode( - filepath.Join(ma.ctx.Dir, "config", "default", "manager_auth_proxy_patch.yaml"), - "- \"--leader-election-id=advanced-molecule-operator\"", + filepath.Join(dir, "config", "default", "manager_auth_proxy_patch.yaml"), + "- \"--leader-elect\"", managerAuthArgs) pkg.CheckError("adding vaulting args to the proxy auth", err) - log.Infof("adding task to not pull image to the config/testing") + log.Info("adding task to not pull image to the config/testing") err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "testing", "kustomization.yaml"), + filepath.Join(dir, "config", "testing", "kustomization.yaml"), "- manager_image.yaml", "- manager_image.yaml\n- pull_policy/Never.yaml") pkg.CheckError("adding task to not pull image to the config/testing", err) } -func (ma *AdvancedMolecule) addMocksFromTestdata() { - log.Infof("adding ansible.cfg") - cmd := exec.Command("cp", "../../../hack/generate/samples/internal/ansible/testdata/ansible.cfg", ma.ctx.Dir) - _, err := ma.ctx.Run(cmd) +func addMocksFromTestdata(dir string, cc command.CommandContext) { + testDataAbsPath, err := filepath.Abs("hack/generate/samples/ansible/testdata") + pkg.CheckError("absolute path for testdata", err) + log.Info("adding ansible.cfg") + cmd := exec.Command("cp", filepath.Join(testDataAbsPath, "ansible.cfg"), dir) + _, err = cc.Run(cmd) pkg.CheckError("adding ansible.cfg", err) - log.Infof("adding plugins/") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/plugins/", filepath.Join(ma.ctx.Dir, "plugins/")) - _, err = ma.ctx.Run(cmd) + log.Info("adding plugins/") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "plugins/"), filepath.Join(dir, "plugins/")) + _, err = cc.Run(cmd) pkg.CheckError("adding plugins/", err) - log.Infof("adding fixture_collection/") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/fixture_collection/", filepath.Join(ma.ctx.Dir, "fixture_collection/")) - _, err = ma.ctx.Run(cmd) + log.Info("adding fixture_collection/") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "fixture_collection/"), filepath.Join(dir, "fixture_collection/")) + _, err = cc.Run(cmd) pkg.CheckError("adding fixture_collection/", err) - log.Infof("replacing watches.yaml") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/watches.yaml", ma.ctx.Dir) - _, err = ma.ctx.Run(cmd) + log.Info("replacing watches.yaml") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "watches.yaml"), dir) + _, err = cc.Run(cmd) pkg.CheckError("replacing watches.yaml", err) - log.Infof("adding tasks/") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/tasks/", filepath.Join(ma.ctx.Dir, "molecule/default/")) - _, err = ma.ctx.Run(cmd) + log.Info("adding tasks/") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "tasks/"), filepath.Join(dir, "molecule/default/")) + _, err = cc.Run(cmd) pkg.CheckError("adding tasks/", err) - log.Infof("adding secret playbook") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/secret.yml", filepath.Join(ma.ctx.Dir, "playbooks/secret.yml")) - _, err = ma.ctx.Run(cmd) + log.Info("adding secret playbook") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "secret.yml"), filepath.Join(dir, "playbooks/secret.yml")) + _, err = cc.Run(cmd) pkg.CheckError("adding secret playbook", err) - log.Infof("adding inventory/") - cmd = exec.Command("cp", "-r", "../../../hack/generate/samples/internal/ansible/testdata/inventory/", filepath.Join(ma.ctx.Dir, "inventory/")) - _, err = ma.ctx.Run(cmd) + log.Info("adding inventory/") + cmd = exec.Command("cp", "-r", filepath.Join(testDataAbsPath, "inventory/"), filepath.Join(dir, "inventory/")) + _, err = cc.Run(cmd) pkg.CheckError("adding inventory/", err) - log.Infof("adding finalizer for finalizerconcurrencytest") - cmd = exec.Command("cp", "../../../hack/generate/samples/internal/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml", filepath.Join(ma.ctx.Dir, "playbooks/finalizerconcurrencyfinalizer.yml")) - _, err = ma.ctx.Run(cmd) + log.Info("adding finalizer for finalizerconcurrencytest") + cmd = exec.Command("cp", filepath.Join(testDataAbsPath, "/playbooks/finalizerconcurrencyfinalizer.yml"), filepath.Join(dir, "playbooks/finalizerconcurrencyfinalizer.yml")) + _, err = cc.Run(cmd) pkg.CheckError("adding finalizer for finalizerconccurencytest", err) } -func (ma *AdvancedMolecule) updateDockerfile() { - log.Infof("replacing project Dockerfile to use ansible base image with the dev tag") +func updateDockerfile(dir string) { + log.Info("replacing project Dockerfile to use ansible base image with the dev tag") err := kbutil.ReplaceRegexInFile( - filepath.Join(ma.ctx.Dir, "Dockerfile"), + filepath.Join(dir, "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") pkg.CheckError("replacing Dockerfile", err) - log.Infof("inserting code to Dockerfile") + log.Info("inserting code to Dockerfile") const dockerfileFragment = ` # Customizations done to check advanced scenarios @@ -267,14 +222,14 @@ RUN echo abc123 > /opt/ansible/pwd.yml \ && ansible-vault encrypt_string --vault-password-file /opt/ansible/pwd.yml 'thisisatest' --name 'the_secret' > /opt/ansible/vars.yml ` err = kbutil.InsertCode( - filepath.Join(ma.ctx.Dir, "Dockerfile"), + filepath.Join(dir, "Dockerfile"), "COPY playbooks/ ${HOME}/playbooks/", dockerfileFragment) pkg.CheckError("replacing Dockerfile", err) } -func (ma *AdvancedMolecule) updatePlaybooks() { - log.Infof("adding playbook for argstest") +func updatePlaybooks(dir string) { + log.Info("adding playbook for argstest") const argsPlaybook = `--- - hosts: localhost gather_facts: no @@ -297,12 +252,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { msg: The decrypted value is {{the_secret.the_secret}} ` err := kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "argstest.yml"), + filepath.Join(dir, "playbooks", "argstest.yml"), originalPlaybookFragment, argsPlaybook) pkg.CheckError("adding playbook for argstest", err) - log.Infof("adding playbook for casetest") + log.Info("adding playbook for casetest") const casePlaybook = `--- - hosts: localhost gather_facts: no @@ -321,12 +276,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { shouldBeCamel: '{{ camelCaseVar | default("false") }}' ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "casetest.yml"), + filepath.Join(dir, "playbooks", "casetest.yml"), originalPlaybookFragment, casePlaybook) pkg.CheckError("adding playbook for casetest", err) - log.Infof("adding playbook for inventorytest") + log.Info("adding playbook for inventorytest") const inventoryPlaybook = `--- - hosts: test gather_facts: no @@ -340,12 +295,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { - command: echo hello - debug: msg='{{ "hello" | test }}'` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "inventorytest.yml"), + filepath.Join(dir, "playbooks", "inventorytest.yml"), "---\n- hosts: localhost\n gather_facts: no\n collections:\n - kubernetes.core\n - operator_sdk.util\n tasks:\n - import_role:\n name: \"inventorytest\"", inventoryPlaybook) pkg.CheckError("adding playbook for inventorytest", err) - log.Infof("adding playbook for reconciliationtest") + log.Info("adding playbook for reconciliationtest") const reconciliationPlaybook = `--- - hosts: localhost gather_facts: no @@ -398,12 +353,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { when: configmap.resources|length > 0 and (configmap.resources.0.data.iterations|int) < 5 ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "reconciliationtest.yml"), + filepath.Join(dir, "playbooks", "reconciliationtest.yml"), originalPlaybookFragment, reconciliationPlaybook) pkg.CheckError("adding playbook for reconciliationtest", err) - log.Infof("adding playbook for selectortest") + log.Info("adding playbook for selectortest") const selectorPlaybook = `--- - hosts: localhost gather_facts: no @@ -422,12 +377,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { hello: "world" ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "selectortest.yml"), + filepath.Join(dir, "playbooks", "selectortest.yml"), originalPlaybookFragment, selectorPlaybook) pkg.CheckError("adding playbook for selectortest", err) - log.Infof("adding playbook for subresourcestest") + log.Info("adding playbook for subresourcestest") const subresourcesPlaybook = `--- - hosts: localhost gather_facts: no @@ -479,12 +434,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { logs: '{{ log_result.log }}' ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "subresourcestest.yml"), + filepath.Join(dir, "playbooks", "subresourcestest.yml"), originalPlaybookFragment, subresourcesPlaybook) pkg.CheckError("adding playbook for subresourcestest", err) - log.Infof("adding playbook for clusterannotationtest") + log.Info("adding playbook for clusterannotationtest") const clusterAnnotationTest = `--- - hosts: localhost gather_facts: no @@ -514,12 +469,12 @@ func (ma *AdvancedMolecule) updatePlaybooks() { foo: bar ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "clusterannotationtest.yml"), + filepath.Join(dir, "playbooks", "clusterannotationtest.yml"), originalPlaybookFragment, clusterAnnotationTest) pkg.CheckError("adding playbook for clusterannotationtest", err) - log.Infof("adding playbook for finalizerconcurrencytest") + log.Info("adding playbook for finalizerconcurrencytest") const finalizerConcurrencyTest = `--- - hosts: localhost gather_facts: no @@ -545,41 +500,20 @@ func (ma *AdvancedMolecule) updatePlaybooks() { msg: "Unpause!" ` err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "playbooks", "finalizerconcurrencytest.yml"), + filepath.Join(dir, "playbooks", "finalizerconcurrencytest.yml"), originalPlaybookFragment, finalizerConcurrencyTest) pkg.CheckError("adding playbook for finalizerconcurrencytest", err) } -func (ma *AdvancedMolecule) addPlaybooks() { - allPlaybookKinds := []string{ - "ArgsTest", - "CaseTest", - "CollectionTest", - "ClusterAnnotationTest", - "FinalizerConcurrencyTest", - "ReconciliationTest", - "SelectorTest", - "SubresourcesTest", - } - - // Create API - for _, k := range allPlaybookKinds { - logMsgForKind := fmt.Sprintf("creating an API %s", k) - log.Infof(logMsgForKind) - err := ma.ctx.CreateAPI( - "--group", ma.ctx.Group, - "--version", ma.ctx.Version, - "--kind", k, - "--generate-playbook") - pkg.CheckError(logMsgForKind, err) - - k = strings.ToLower(k) +func removeFixmeFromPlaybooks(dir string, gvks []schema.GroupVersionKind) { + for _, gvk := range gvks { + k := strings.ToLower(gvk.Kind) task := fmt.Sprintf("%s_test.yml", k) - logMsgForKind = fmt.Sprintf("removing FIXME assert from %s", task) - log.Infof(logMsgForKind) - err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", task), + logMsgForKind := fmt.Sprintf("removing FIXME assert from %s", task) + log.Info(logMsgForKind) + err := kbutil.ReplaceInFile( + filepath.Join(dir, "molecule", "default", "tasks", task), fixmeAssert, "") pkg.CheckError(logMsgForKind, err) diff --git a/hack/generate/samples/internal/ansible/constants.go b/hack/generate/samples/ansible/constants.go similarity index 100% rename from hack/generate/samples/internal/ansible/constants.go rename to hack/generate/samples/ansible/constants.go diff --git a/hack/generate/samples/ansible/generate.go b/hack/generate/samples/ansible/generate.go new file mode 100644 index 0000000..3dad664 --- /dev/null +++ b/hack/generate/samples/ansible/generate.go @@ -0,0 +1,243 @@ +// Copyright 2021 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ansible + +import ( + "os" + "path/filepath" + + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" + "github.com/operator-framework/ansible-operator-plugins/pkg/plugins/ansible/v1" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" + samplecli "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample/cli" + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/kubebuilder/v3/pkg/cli" + cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" + "sigs.k8s.io/kubebuilder/v3/pkg/plugin" + kustomizev2Alpha "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v2-alpha" + "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang" +) + +const bundleImage = "quay.io/example/memcached-operator:v0.0.1" + +var memcachedGVK = schema.GroupVersionKind{ + Group: "cache", + Version: "v1alpha1", + Kind: "Memcached", +} + +func getCli() *cli.CLI { + ansibleBundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, ansible.Plugin{}.Version(), + kustomizev2Alpha.Plugin{}, + ansible.Plugin{}, + ) + + c, err := cli.New( + cli.WithCommandName("cli"), + cli.WithVersion("v0.0.0"), + cli.WithPlugins( + ansibleBundle, + ), + cli.WithDefaultPlugins(cfgv3.Version, ansibleBundle), + cli.WithDefaultProjectVersion(cfgv3.Version), + cli.WithCompletion(), + ) + pkg.CheckError("getting cli implementation:", err) + return c +} + +func GenerateMemcachedSamples(rootPath string) []sample.Sample { + ansibleCC := command.NewGenericCommandContext( + command.WithEnv("GO111MODULE=on"), + command.WithDir(filepath.Join(rootPath, "ansible")), + ) + + ansibleMemcached, err := samplecli.NewCliSample( + samplecli.WithCLI(getCli()), + samplecli.WithCommandContext(ansibleCC), + samplecli.WithDomain("example.com"), + samplecli.WithGvk(memcachedGVK), + samplecli.WithPlugins("ansible"), + samplecli.WithExtraApiOptions("--generate-role", "--generate-playbook"), + samplecli.WithName("memcached-operator"), + ) + pkg.CheckError("attempting to create sample cli", err) + + // remove sample directory if it already exists + err = os.RemoveAll(ansibleMemcached.Dir()) + pkg.CheckError("attempting to remove sample dir", err) + + gen := sample.NewGenerator( + sample.WithNoWebhook(), + ) + + err = gen.GenerateSamples(ansibleMemcached) + pkg.CheckError("generating ansible samples", err) + + ImplementMemcached(ansibleMemcached, bundleImage) + return []sample.Sample{ansibleMemcached} +} + +// GenerateMoleculeSample will call all actions to create the directory and generate the sample +// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT +// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files +// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext. +func GenerateMoleculeSample(samplesPath string) []sample.Sample { + ansibleCC := command.NewGenericCommandContext( + command.WithEnv("GO111MODULE=on"), + command.WithDir(filepath.Join(samplesPath, "")), + ) + + ansibleMoleculeMemcached, err := samplecli.NewCliSample( + samplecli.WithCLI(getCli()), + samplecli.WithCommandContext(ansibleCC), + samplecli.WithDomain("example.com"), + samplecli.WithGvk( + memcachedGVK, + schema.GroupVersionKind{ + Group: memcachedGVK.Group, + Version: memcachedGVK.Version, + Kind: "Foo", + }, + schema.GroupVersionKind{ + Group: memcachedGVK.Group, + Version: memcachedGVK.Version, + Kind: "Memfin", + }, + ), + samplecli.WithPlugins("ansible"), + samplecli.WithExtraApiOptions("--generate-role", "--generate-playbook"), + samplecli.WithName("memcached-molecule-operator"), + ) + pkg.CheckError("attempting to create sample cli", err) + + addIgnore, err := samplecli.NewCliSample( + samplecli.WithCLI(getCli()), + samplecli.WithCommandContext(ansibleMoleculeMemcached.CommandContext()), + samplecli.WithGvk( + schema.GroupVersionKind{ + Group: "ignore", + Version: "v1", + Kind: "Secret", + }, + ), + samplecli.WithPlugins("ansible"), + samplecli.WithExtraApiOptions("--generate-role"), + samplecli.WithName(ansibleMoleculeMemcached.Name()), + ) + pkg.CheckError("creating ignore samples", err) + + // remove sample directory if it already exists + err = os.RemoveAll(ansibleMoleculeMemcached.Dir()) + pkg.CheckError("attempting to remove sample dir", err) + + gen := sample.NewGenerator( + sample.WithNoWebhook(), + ) + + err = gen.GenerateSamples(ansibleMoleculeMemcached) + pkg.CheckError("generating ansible molecule sample", err) + + log.Infof("enabling multigroup support") + err = e2e.AllowProjectBeMultiGroup(ansibleMoleculeMemcached) + pkg.CheckError("updating PROJECT file", err) + + ignoreGen := sample.NewGenerator(sample.WithNoInit(), sample.WithNoWebhook()) + err = ignoreGen.GenerateSamples(addIgnore) + pkg.CheckError("generating ansible molecule sample - ignore", err) + + ImplementMemcached(ansibleMoleculeMemcached, bundleImage) + + ImplementMemcachedMolecule(ansibleMoleculeMemcached, bundleImage) + return []sample.Sample{ansibleMoleculeMemcached} +} + +// GenerateAdvancedMoleculeSample will call all actions to create the directory and generate the sample +// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT +// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files +// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext. +func GenerateAdvancedMoleculeSample(samplesPath string) { + ansibleCC := command.NewGenericCommandContext( + command.WithEnv("GO111MODULE=on"), + command.WithDir(filepath.Join(samplesPath, "")), + ) + + gv := schema.GroupVersion{ + Group: "test", + Version: "v1alpha1", + } + + kinds := []string{ + "ArgsTest", + "CaseTest", + "CollectionTest", + "ClusterAnnotationTest", + "FinalizerConcurrencyTest", + "ReconciliationTest", + "SelectorTest", + "SubresourcesTest", + } + + var gvks []schema.GroupVersionKind + + for _, kind := range kinds { + gvks = append(gvks, schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}) + } + + advancedMoleculeMemcached, err := samplecli.NewCliSample( + samplecli.WithCLI(getCli()), + samplecli.WithCommandContext(ansibleCC), + samplecli.WithDomain("example.com"), + samplecli.WithGvk(gvks...), + samplecli.WithPlugins("ansible"), + samplecli.WithExtraApiOptions("--generate-playbook"), + samplecli.WithName("advanced-molecule-operator"), + ) + pkg.CheckError("attempting to create sample cli", err) + + addInventory, err := samplecli.NewCliSample( + samplecli.WithCLI(getCli()), + samplecli.WithCommandContext(advancedMoleculeMemcached.CommandContext()), + samplecli.WithGvk( + schema.GroupVersionKind{ + Group: gv.Group, + Version: gv.Version, + Kind: "InventoryTest", + }, + ), + samplecli.WithPlugins("ansible"), + samplecli.WithExtraApiOptions("--generate-role", "--generate-playbook"), + samplecli.WithName(advancedMoleculeMemcached.Name()), + ) + pkg.CheckError("creating inventory samples", err) + + // remove sample directory if it already exists + err = os.RemoveAll(advancedMoleculeMemcached.Dir()) + pkg.CheckError("attempting to remove sample dir", err) + + gen := sample.NewGenerator(sample.WithNoWebhook()) + + err = gen.GenerateSamples(advancedMoleculeMemcached) + pkg.CheckError("generating ansible advanced molecule sample", err) + + ignoreGen := sample.NewGenerator(sample.WithNoInit(), sample.WithNoWebhook()) + err = ignoreGen.GenerateSamples(addInventory) + pkg.CheckError("generating ansible molecule sample - ignore", err) + + ImplementAdvancedMolecule(advancedMoleculeMemcached, bundleImage) +} diff --git a/hack/generate/samples/ansible/memcached.go b/hack/generate/samples/ansible/memcached.go new file mode 100644 index 0000000..e7cfc24 --- /dev/null +++ b/hack/generate/samples/ansible/memcached.go @@ -0,0 +1,75 @@ +// Copyright 2020 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ansible + +import ( + "fmt" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime/schema" + kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" + + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" +) + +func ImplementMemcached(sample sample.Sample, image string) { + log.Info("customizing the sample") + err := kbutil.UncommentCode( + filepath.Join(sample.Dir(), "config", "default", "kustomization.yaml"), + "#- ../prometheus", "#") + pkg.CheckError("enabling prometheus metrics", err) + + for _, gvk := range sample.GVKs() { + if gvk.Kind == "Memcached" { + addingAnsibleTask(sample.Dir(), gvk) + addingMoleculeMockData(sample.Dir(), sample.Name(), gvk) + } + } +} + +// addingMoleculeMockData will customize the molecule data +func addingMoleculeMockData(dir string, projectName string, gvk schema.GroupVersionKind) { + log.Info("adding molecule test for Ansible task") + moleculeTaskPath := filepath.Join(dir, "molecule", "default", "tasks", + fmt.Sprintf("%s_test.yml", strings.ToLower(gvk.Kind))) + + err := kbutil.ReplaceInFile(moleculeTaskPath, + originaMemcachedMoleculeTask, fmt.Sprintf(moleculeTaskFragment, projectName, projectName)) + pkg.CheckError("replacing molecule default tasks", err) +} + +// addingAnsibleTask will add the Ansible Task and update the sample +func addingAnsibleTask(dir string, gvk schema.GroupVersionKind) { + log.Info("adding Ansible task and variable") + err := kbutil.InsertCode(filepath.Join(dir, "roles", strings.ToLower(gvk.Kind), + "tasks", "main.yml"), + fmt.Sprintf("# tasks file for %s", gvk.Kind), + roleFragment) + pkg.CheckError("adding task", err) + + err = kbutil.ReplaceInFile(filepath.Join(dir, "roles", strings.ToLower(gvk.Kind), + "defaults", "main.yml"), + fmt.Sprintf("# defaults file for %s", gvk.Kind), + defaultsFragment) + pkg.CheckError("adding defaulting", err) + + err = kbutil.ReplaceInFile(filepath.Join(dir, "config", "samples", + fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))), + "# TODO(user): Add fields here", "size: 1") + pkg.CheckError("updating sample CR", err) +} diff --git a/hack/generate/samples/ansible/memcached_molecule.go b/hack/generate/samples/ansible/memcached_molecule.go new file mode 100644 index 0000000..e3936d4 --- /dev/null +++ b/hack/generate/samples/ansible/memcached_molecule.go @@ -0,0 +1,128 @@ +// Copyright 2020 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ansible + +import ( + "fmt" + "io/ioutil" + "os/exec" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" + kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" + + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" +) + +func ImplementMemcachedMolecule(sample sample.Sample, image string) { + + for _, gvk := range sample.GVKs() { + moleculeTaskPath := filepath.Join(sample.Dir(), "molecule", "default", "tasks", + fmt.Sprintf("%s_test.yml", strings.ToLower(gvk.Kind))) + + fmt.Println("MOLECULE TASK PATH:", moleculeTaskPath) + + if gvk.Kind == "Memcached" { + log.Info("insert molecule task to ensure that ConfigMap will be deleted") + err := kbutil.InsertCode(moleculeTaskPath, targetMoleculeCheckDeployment, molecuTaskToCheckConfigMap) + pkg.CheckError("replacing memcached task to add config map check", err) + + log.Info("insert molecule task to ensure to check secret") + err = kbutil.InsertCode(moleculeTaskPath, memcachedCustomStatusMoleculeTarget, testSecretMoleculeCheck) + pkg.CheckError("replacing memcached task to add secret check", err) + + log.Info("insert molecule task to ensure to foo ") + err = kbutil.InsertCode(moleculeTaskPath, testSecretMoleculeCheck, testFooMoleculeCheck) + pkg.CheckError("replacing memcached task to add foo check", err) + + log.Info("insert molecule task to check custom metrics") + err = kbutil.InsertCode(moleculeTaskPath, testFooMoleculeCheck, customMetricsTest) + + pkg.CheckError("replacing memcached task to add foo check", err) + + log.Info("adding Memcached mock task to the role with black list") + err = kbutil.InsertCode(filepath.Join(sample.Dir(), "roles", strings.ToLower(gvk.Kind), "tasks", "main.yml"), + roleFragment, memcachedWithBlackListTask) + pkg.CheckError("replacing in tasks/main.yml", err) + } + + if gvk.Kind != "Memcached" { + log.Info("updating spec of kind: ", gvk.Kind) + err := kbutil.ReplaceInFile( + filepath.Join(sample.Dir(), "config", "samples", fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))), + "# TODO(user): Add fields here", + "foo: bar") + pkg.CheckError("updating spec of "+fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind)), err) + + log.Infof("removing FIXME asserts from %s_test.yml", strings.ToLower(gvk.Kind)) + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "molecule", "default", "tasks", fmt.Sprintf("%s_test.yml", strings.ToLower(gvk.Kind))), + fixmeAssert, "") + pkg.CheckError(fmt.Sprintf("replacing %s_test.yml", strings.ToLower(gvk.Kind)), err) + } + + } + + log.Info("replacing project Dockerfile to use ansible base image with the dev tag") + err := kbutil.ReplaceRegexInFile(filepath.Join(sample.Dir(), "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") + pkg.CheckError("replacing Dockerfile", err) + + log.Info("adding RBAC permissions") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "config", "rbac", "role.yaml"), + "#+kubebuilder:scaffold:rules", rolesForBaseOperator) + pkg.CheckError("replacing in role.yml", err) + + log.Info("adding task to delete config map") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "roles", "memfin", "tasks", "main.yml"), + "# tasks file for Memfin", taskToDeleteConfigMap) + pkg.CheckError("replacing in tasks/main.yml", err) + + log.Info("adding to watches finalizer and blacklist") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "watches.yaml"), + "playbook: playbooks/memcached.yml", memcachedWatchCustomizations) + pkg.CheckError("replacing in watches", err) + + log.Info("removing ignore group for the secret from watches as an workaround to work with core types") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "watches.yaml"), + "ignore.example.com", "\"\"") + pkg.CheckError("replacing the watches file", err) + + log.Info("removing molecule test for the Secret since it is a core type") + cmd := exec.Command("rm", "-rf", filepath.Join(sample.Dir(), "molecule", "default", "tasks", "secret_test.yml")) + _, err = sample.CommandContext().Run(cmd) + pkg.CheckError("removing secret test file", err) + + log.Info("adding Secret task to the role") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "roles", "secret", "tasks", "main.yml"), + originalTaskSecret, taskForSecret) + pkg.CheckError("replacing in secret/tasks/main.yml file", err) + + log.Info("adding ManageStatus == false for role secret") + err = kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "watches.yaml"), + "role: secret", manageStatusFalseForRoleSecret) + pkg.CheckError("replacing in watches.yaml", err) + + // prevent high load of controller caused by watching all the secrets in the cluster + watchNamespacePatchFileName := "watch_namespace_patch.yaml" + log.Info("adding WATCH_NAMESPACE env patch to watch own namespace") + err = ioutil.WriteFile(filepath.Join(sample.Dir(), "config", "testing", watchNamespacePatchFileName), []byte(watchNamespacePatch), 0644) + pkg.CheckError("adding watch_namespace_patch.yaml", err) + + log.Info("adding WATCH_NAMESPACE env patch to patch list to be applied") + err = kbutil.InsertCode(filepath.Join(sample.Dir(), "config", "testing", "kustomization.yaml"), "patchesStrategicMerge:", + fmt.Sprintf("\n- %s", watchNamespacePatchFileName)) + pkg.CheckError("inserting in kustomization.yaml", err) +} diff --git a/hack/generate/samples/internal/ansible/testdata/ansible.cfg b/hack/generate/samples/ansible/testdata/ansible.cfg similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/ansible.cfg rename to hack/generate/samples/ansible/testdata/ansible.cfg diff --git a/hack/generate/samples/internal/ansible/testdata/fixture_collection/galaxy.yml b/hack/generate/samples/ansible/testdata/fixture_collection/galaxy.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/fixture_collection/galaxy.yml rename to hack/generate/samples/ansible/testdata/fixture_collection/galaxy.yml diff --git a/hack/generate/samples/internal/ansible/testdata/fixture_collection/roles/dummy/tasks/main.yml b/hack/generate/samples/ansible/testdata/fixture_collection/roles/dummy/tasks/main.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/fixture_collection/roles/dummy/tasks/main.yml rename to hack/generate/samples/ansible/testdata/fixture_collection/roles/dummy/tasks/main.yml diff --git a/hack/generate/samples/internal/ansible/testdata/inventory/group_vars/test.yml b/hack/generate/samples/ansible/testdata/inventory/group_vars/test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/inventory/group_vars/test.yml rename to hack/generate/samples/ansible/testdata/inventory/group_vars/test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/inventory/hosts b/hack/generate/samples/ansible/testdata/inventory/hosts similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/inventory/hosts rename to hack/generate/samples/ansible/testdata/inventory/hosts diff --git a/hack/generate/samples/internal/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml b/hack/generate/samples/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml rename to hack/generate/samples/ansible/testdata/playbooks/finalizerconcurrencyfinalizer.yml diff --git a/hack/generate/samples/internal/ansible/testdata/plugins/filter/test.py b/hack/generate/samples/ansible/testdata/plugins/filter/test.py similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/plugins/filter/test.py rename to hack/generate/samples/ansible/testdata/plugins/filter/test.py diff --git a/hack/generate/samples/internal/ansible/testdata/secret.yml b/hack/generate/samples/ansible/testdata/secret.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/secret.yml rename to hack/generate/samples/ansible/testdata/secret.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/argstest_test.yml b/hack/generate/samples/ansible/testdata/tasks/argstest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/argstest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/argstest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/casetest_test.yml b/hack/generate/samples/ansible/testdata/tasks/casetest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/casetest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/casetest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/clusterannotationtest_test.yml b/hack/generate/samples/ansible/testdata/tasks/clusterannotationtest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/clusterannotationtest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/clusterannotationtest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/collectiontest_test.yml b/hack/generate/samples/ansible/testdata/tasks/collectiontest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/collectiontest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/collectiontest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/finalizerconcurrencytest_test.yml b/hack/generate/samples/ansible/testdata/tasks/finalizerconcurrencytest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/finalizerconcurrencytest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/finalizerconcurrencytest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/inventorytest_test.yml b/hack/generate/samples/ansible/testdata/tasks/inventorytest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/inventorytest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/inventorytest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/reconciliationtest_test.yml b/hack/generate/samples/ansible/testdata/tasks/reconciliationtest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/reconciliationtest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/reconciliationtest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/secretstest_test.yml b/hack/generate/samples/ansible/testdata/tasks/secretstest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/secretstest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/secretstest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/selectortest_test.yml b/hack/generate/samples/ansible/testdata/tasks/selectortest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/selectortest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/selectortest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/tasks/subresourcestest_test.yml b/hack/generate/samples/ansible/testdata/tasks/subresourcestest_test.yml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/tasks/subresourcestest_test.yml rename to hack/generate/samples/ansible/testdata/tasks/subresourcestest_test.yml diff --git a/hack/generate/samples/internal/ansible/testdata/watches.yaml b/hack/generate/samples/ansible/testdata/watches.yaml similarity index 100% rename from hack/generate/samples/internal/ansible/testdata/watches.yaml rename to hack/generate/samples/ansible/testdata/watches.yaml diff --git a/hack/generate/samples/generate_testdata.go b/hack/generate/samples/generate_testdata.go index 6d82be2..ebf07ec 100644 --- a/hack/generate/samples/generate_testdata.go +++ b/hack/generate/samples/generate_testdata.go @@ -19,7 +19,7 @@ import ( "os" "path/filepath" - "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/ansible" + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/ansible" log "github.com/sirupsen/logrus" @@ -53,5 +53,5 @@ func main() { log.Infof("writing sample directories under %s", samplesPath) log.Infof("creating Ansible Memcached Sample") - ansible.GenerateMemcachedSamples(binaryPath, samplesPath) + ansible.GenerateMoleculeSample(samplesPath) } diff --git a/hack/generate/samples/internal/ansible/generate.go b/hack/generate/samples/internal/ansible/generate.go deleted file mode 100644 index 2d0321e..0000000 --- a/hack/generate/samples/internal/ansible/generate.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2021 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ansible - -import ( - "path/filepath" -) - -func GenerateMemcachedSamples(binaryPath, rootPath string) { - GenerateMemcachedSample(binaryPath, filepath.Join(rootPath, "ansible")) -} diff --git a/hack/generate/samples/internal/ansible/memcached.go b/hack/generate/samples/internal/ansible/memcached.go deleted file mode 100644 index f2d0b37..0000000 --- a/hack/generate/samples/internal/ansible/memcached.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ansible - -import ( - "fmt" - "path/filepath" - "strings" - - log "github.com/sirupsen/logrus" - kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" - - "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" -) - -// Memcached defines the context for the sample -type Memcached struct { - ctx *pkg.SampleContext -} - -// GenerateMemcachedSample will call all actions to create the directory and generate the sample -// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT -// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files -// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext. -func GenerateMemcachedSample(binaryPath, samplesPath string) { - ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-operator"), - "GO111MODULE=on") - pkg.CheckError("generating Ansible memcached context", err) - - memcached := Memcached{&ctx} - memcached.Prepare() - memcached.Run() -} - -// Prepare the Context for the Memcached Ansible Sample -// Note that sample directory will be re-created and the context data for the sample -// will be set such as the domain and GVK. -func (ma *Memcached) Prepare() { - log.Infof("destroying directory for memcached Ansible samples") - ma.ctx.Destroy() - - log.Infof("creating directory") - err := ma.ctx.Prepare() - pkg.CheckError("creating directory for Ansible Sample", err) - - log.Infof("setting domain and GVK") - ma.ctx.Domain = "example.com" - ma.ctx.Version = "v1alpha1" - ma.ctx.Group = "cache" - ma.ctx.Kind = "Memcached" -} - -// Run the steps to create the Memcached Ansible Sample -func (ma *Memcached) Run() { - log.Infof("creating the project") - err := ma.ctx.Init( - "--plugins", "ansible", - "--group", ma.ctx.Group, - "--version", ma.ctx.Version, - "--kind", ma.ctx.Kind, - "--domain", ma.ctx.Domain, - "--generate-role", - "--generate-playbook") - pkg.CheckError("creating the project", err) - - log.Infof("customizing the sample") - err = kbutil.UncommentCode( - filepath.Join(ma.ctx.Dir, "config", "default", "kustomization.yaml"), - "#- ../prometheus", "#") - pkg.CheckError("enabling prometheus metrics", err) - - err = ma.ctx.UncommentRestrictivePodStandards() - pkg.CheckError("creating the bundle", err) - - ma.addingAnsibleTask() - ma.addingMoleculeMockData() - - log.Infof("creating the bundle") - err = ma.ctx.GenerateBundle() - pkg.CheckError("creating the bundle", err) - - log.Infof("striping bundle annotations") - err = ma.ctx.StripBundleAnnotations() - pkg.CheckError("striping bundle annotations", err) - - log.Infof("setting createdAt annotation") - csv := filepath.Join(ma.ctx.Dir, "bundle", "manifests", ma.ctx.ProjectName+".clusterserviceversion.yaml") - err = kbutil.ReplaceRegexInFile(csv, "createdAt:.*", createdAt) - pkg.CheckError("setting createdAt annotation", err) -} - -// addingMoleculeMockData will customize the molecule data -func (ma *Memcached) addingMoleculeMockData() { - log.Infof("adding molecule test for Ansible task") - moleculeTaskPath := filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", - fmt.Sprintf("%s_test.yml", strings.ToLower(ma.ctx.Kind))) - - err := kbutil.ReplaceInFile(moleculeTaskPath, - originaMemcachedMoleculeTask, fmt.Sprintf(moleculeTaskFragment, ma.ctx.ProjectName, ma.ctx.ProjectName)) - pkg.CheckError("replacing molecule default tasks", err) -} - -// addingAnsibleTask will add the Ansible Task and update the sample -func (ma *Memcached) addingAnsibleTask() { - log.Infof("adding Ansible task and variable") - err := kbutil.InsertCode(filepath.Join(ma.ctx.Dir, "roles", strings.ToLower(ma.ctx.Kind), - "tasks", "main.yml"), - fmt.Sprintf("# tasks file for %s", ma.ctx.Kind), - roleFragment) - pkg.CheckError("adding task", err) - - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "roles", strings.ToLower(ma.ctx.Kind), - "defaults", "main.yml"), - fmt.Sprintf("# defaults file for %s", ma.ctx.Kind), - defaultsFragment) - pkg.CheckError("adding defaulting", err) - - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "config", "samples", - fmt.Sprintf("%s_%s_%s.yaml", ma.ctx.Group, ma.ctx.Version, strings.ToLower(ma.ctx.Kind))), - "# TODO(user): Add fields here", "size: 1") - pkg.CheckError("updating sample CR", err) -} - -const createdAt = `createdAt: "2022-11-08T17:26:37Z"` diff --git a/hack/generate/samples/internal/ansible/memcached_molecule.go b/hack/generate/samples/internal/ansible/memcached_molecule.go deleted file mode 100644 index 053c76a..0000000 --- a/hack/generate/samples/internal/ansible/memcached_molecule.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ansible - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - log "github.com/sirupsen/logrus" - kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" - - "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/pkg" -) - -// MemcachedMolecule defines the context for the sample -type MemcachedMolecule struct { - ctx *pkg.SampleContext - Base Memcached -} - -// GenerateMoleculeSample will call all actions to create the directory and generate the sample -// The Context to run the samples are not the same in the e2e test. In this way, note that it should NOT -// be called in the e2e tests since it will call the Prepare() to set the sample context and generate the files -// in the testdata directory. The e2e tests only ought to use the Run() method with the TestContext. -func GenerateMoleculeSample(binaryPath, samplesPath string) { - ctx, err := pkg.NewSampleContext(binaryPath, filepath.Join(samplesPath, "memcached-molecule-operator"), - "GO111MODULE=on") - pkg.CheckError("generating Ansible Moleule memcached context", err) - - molecule := MemcachedMolecule{ctx: &ctx, Base: Memcached{&ctx}} - molecule.Prepare() - molecule.Run() -} - -// Prepare the Context for the Memcached Ansible Sample -// Note that sample directory will be re-created and the context data for the sample -// will be set such as the domain and GVK. -func (ma *MemcachedMolecule) Prepare() { - log.Infof("destroying directory for memcached Ansible samples") - ma.ctx.Destroy() - - log.Infof("creating directory") - err := ma.ctx.Prepare() - pkg.CheckError("creating directory for Ansible Sample", err) - - log.Infof("setting domain and GVK") - ma.ctx.Domain = "example.com" - ma.ctx.Version = "v1alpha1" - ma.ctx.Group = "cache" - ma.ctx.Kind = "Memcached" -} - -// Run the steps to create the Memcached Ansible Sample -func (ma *MemcachedMolecule) Run() { - ma.Base.Run() - - moleculeTaskPath := filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", - fmt.Sprintf("%s_test.yml", strings.ToLower(ma.ctx.Kind))) - - log.Infof("insert molecule task to ensure that ConfigMap will be deleted") - err := kbutil.InsertCode(moleculeTaskPath, targetMoleculeCheckDeployment, molecuTaskToCheckConfigMap) - pkg.CheckError("replacing memcached task to add config map check", err) - - log.Infof("insert molecule task to ensure to check secret") - err = kbutil.InsertCode(moleculeTaskPath, memcachedCustomStatusMoleculeTarget, testSecretMoleculeCheck) - pkg.CheckError("replacing memcached task to add secret check", err) - - log.Infof("insert molecule task to ensure to foo ") - err = kbutil.InsertCode(moleculeTaskPath, testSecretMoleculeCheck, testFooMoleculeCheck) - pkg.CheckError("replacing memcached task to add foo check", err) - - log.Infof("insert molecule task to check custom metrics") - err = kbutil.InsertCode(moleculeTaskPath, testFooMoleculeCheck, customMetricsTest) - - pkg.CheckError("replacing memcached task to add foo check", err) - - log.Infof("replacing project Dockerfile to use ansible base image with the dev tag") - err = kbutil.ReplaceRegexInFile(filepath.Join(ma.ctx.Dir, "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") - pkg.CheckError("replacing Dockerfile", err) - - log.Infof("adding RBAC permissions") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "config", "rbac", "role.yaml"), - "#+kubebuilder:scaffold:rules", rolesForBaseOperator) - pkg.CheckError("replacing in role.yml", err) - - log.Infof("adding Memcached mock task to the role with black list") - err = kbutil.InsertCode(filepath.Join(ma.ctx.Dir, "roles", strings.ToLower(ma.ctx.Kind), "tasks", "main.yml"), - roleFragment, memcachedWithBlackListTask) - pkg.CheckError("replacing in tasks/main.yml", err) - - log.Infof("creating an API definition Foo") - err = ma.ctx.CreateAPI( - "--group", ma.ctx.Group, - "--version", ma.ctx.Version, - "--kind", "Foo", - "--generate-role") - pkg.CheckError("creating api", err) - - log.Infof("updating spec of foo sample") - err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "samples", "cache_v1alpha1_foo.yaml"), - "# TODO(user): Add fields here", - "foo: bar") - pkg.CheckError("updating spec of cache_v1alpha1_foo.yaml", err) - - log.Infof("creating an API definition to add a task to delete the config map") - err = ma.ctx.CreateAPI( - "--group", ma.ctx.Group, - "--version", ma.ctx.Version, - "--kind", "Memfin", - "--generate-role") - pkg.CheckError("creating api", err) - - log.Infof("updating spec of Memfin sample") - err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "samples", "cache_v1alpha1_memfin.yaml"), - "# TODO(user): Add fields here", - "foo: bar") - pkg.CheckError("updating spec of cache_v1alpha1_memfin.yaml ", err) - - log.Infof("adding task to delete config map") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "roles", "memfin", "tasks", "main.yml"), - "# tasks file for Memfin", taskToDeleteConfigMap) - pkg.CheckError("replacing in tasks/main.yml", err) - - log.Infof("adding to watches finalizer and blacklist") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "watches.yaml"), - "playbook: playbooks/memcached.yml", memcachedWatchCustomizations) - pkg.CheckError("replacing in watches", err) - - log.Infof("enabling multigroup support") - err = ma.ctx.AllowProjectBeMultiGroup() - pkg.CheckError("updating PROJECT file", err) - - log.Infof("creating core Secret API") - err = ma.ctx.CreateAPI( - // the tool do not allow we crate an API with a group nil for v2+ - // which is required here to mock the tests. - // however, it is done already for v3+. More info: https://github.com/kubernetes-sigs/kubebuilder/issues/1404 - // and the tests should be changed when the tool allows we create API's for core types. - // todo: replace the ignore value when the tool provide a solution for it. - "--group", "ignore", - "--version", "v1", - "--kind", "Secret", - "--generate-role") - pkg.CheckError("creating api", err) - - log.Infof("updating spec of ignore sample") - err = kbutil.ReplaceInFile( - filepath.Join(ma.ctx.Dir, "config", "samples", "ignore_v1_secret.yaml"), - "# TODO(user): Add fields here", - "foo: bar") - pkg.CheckError("updating spec of ignore_v1_secret.yaml", err) - - log.Infof("removing ignore group for the secret from watches as an workaround to work with core types") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "watches.yaml"), - "ignore.example.com", "\"\"") - pkg.CheckError("replacing the watches file", err) - - log.Infof("removing molecule test for the Secret since it is a core type") - cmd := exec.Command("rm", "-rf", filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", "secret_test.yml")) - _, err = ma.ctx.Run(cmd) - pkg.CheckError("removing secret test file", err) - - log.Infof("adding Secret task to the role") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "roles", "secret", "tasks", "main.yml"), - originalTaskSecret, taskForSecret) - pkg.CheckError("replacing in secret/tasks/main.yml file", err) - - log.Infof("adding ManageStatus == false for role secret") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "watches.yaml"), - "role: secret", manageStatusFalseForRoleSecret) - pkg.CheckError("replacing in watches.yaml", err) - - // prevent high load of controller caused by watching all the secrets in the cluster - watchNamespacePatchFileName := "watch_namespace_patch.yaml" - log.Info("adding WATCH_NAMESPACE env patch to watch own namespace") - err = os.WriteFile(filepath.Join(ma.ctx.Dir, "config", "testing", watchNamespacePatchFileName), []byte(watchNamespacePatch), 0644) - pkg.CheckError("adding watch_namespace_patch.yaml", err) - - log.Info("adding WATCH_NAMESPACE env patch to patch list to be applied") - err = kbutil.InsertCode(filepath.Join(ma.ctx.Dir, "config", "testing", "kustomization.yaml"), "patchesStrategicMerge:", - fmt.Sprintf("\n- %s", watchNamespacePatchFileName)) - pkg.CheckError("inserting in kustomization.yaml", err) - - log.Infof("removing FIXME asserts from memfin_test.yml") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", "memfin_test.yml"), - fixmeAssert, "") - pkg.CheckError("replacing memfin_test.yml", err) - - log.Infof("removing FIXME asserts from foo_test.yml") - err = kbutil.ReplaceInFile(filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", "foo_test.yml"), - fixmeAssert, "") - pkg.CheckError("replacing foo_test.yml", err) -} diff --git a/hack/generate/samples/molecule/generate.go b/hack/generate/samples/molecule/generate.go index d9406d6..5e12e44 100644 --- a/hack/generate/samples/molecule/generate.go +++ b/hack/generate/samples/molecule/generate.go @@ -22,7 +22,7 @@ import ( log "github.com/sirupsen/logrus" - "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/internal/ansible" + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/ansible" "github.com/operator-framework/ansible-operator-plugins/internal/testutils" ) @@ -69,10 +69,10 @@ func main() { log.Infof("creating Ansible Molecule Mock Samples under %s", samplesRoot) if sample == "" || sample == "memcached" { - ansible.GenerateMoleculeSample(binaryPath, samplesRoot) + ansible.GenerateMoleculeSample(samplesRoot) } if sample == "" || sample == "advanced" { - ansible.GenerateAdvancedMoleculeSample(binaryPath, samplesRoot) + ansible.GenerateAdvancedMoleculeSample(samplesRoot) } } diff --git a/internal/version/version.go b/internal/version/version.go index 71ccfee..4c8b41f 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -26,5 +26,7 @@ var ( // most recent operator-sdk release tag such that samples are generated with the correct versions // in a release commit. Once each element that uses this version is moved to a separate repo // and release process, this variable will be removed. - ImageVersion = "unknown" + + // TODO: find a way to make this automated. For now manually update this before releases. + ImageVersion = "v1.31.0" ) diff --git a/pkg/testutils/command/command.go b/pkg/testutils/command/command.go new file mode 100644 index 0000000..f108dd5 --- /dev/null +++ b/pkg/testutils/command/command.go @@ -0,0 +1,106 @@ +package command + +import ( + "fmt" + "io" + "os" + "os/exec" + "strings" +) + +// CommandContext represents the context that commands are run in +type CommandContext interface { + // Env returns the environment variables set for the CommandContext + Env() []string + // Dir returns the directory set for the CommandContext + Dir() string + // Stdin returns the stdin set for the CommandContext + Stdin() io.Reader + + // Run runs a given command and will append any extra paths to the configured directory + Run(cmd *exec.Cmd, path ...string) ([]byte, error) +} + +// GenericCommandContext is a generic implementation of the CommandContext interface +type GenericCommandContext struct { + env []string + dir string + stdin io.Reader +} + +// GenericCommandContextOptions is a function used to configure a GenericCommandContext +type GenericCommandContextOptions func(gcc *GenericCommandContext) + +// WithEnv sets the environment variables that should be used when running commands +func WithEnv(env ...string) GenericCommandContextOptions { + return func(gcc *GenericCommandContext) { + gcc.env = make([]string, len(env)) + copy(gcc.env, env) + } +} + +// WithDir sets the directory that commands should be run in +func WithDir(dir string) GenericCommandContextOptions { + return func(gcc *GenericCommandContext) { + gcc.dir = dir + } +} + +// WithStdin sets the stdin when running commands +func WithStdin(stdin io.Reader) GenericCommandContextOptions { + return func(gcc *GenericCommandContext) { + gcc.stdin = stdin + } +} + +// NewGenericCommandContext creates a new GenericCommandContext that can be configured via GenericCommandContextOptions functions +func NewGenericCommandContext(opts ...GenericCommandContextOptions) *GenericCommandContext { + gcc := &GenericCommandContext{ + dir: "", + stdin: os.Stdin, + } + + for _, opt := range opts { + opt(gcc) + } + + return gcc +} + +// Run runs a given command and will append any extra paths to the configured directory +func (gcc *GenericCommandContext) Run(cmd *exec.Cmd, path ...string) ([]byte, error) { + + dir := strings.Join(append([]string{gcc.dir}, path...), "/") + // make the directory if it does not already exist + if dir != "" { + if _, err := os.Stat(dir); os.IsNotExist(err) { + os.MkdirAll(dir, 0755) + } + } + + cmd.Dir = dir + cmd.Env = append(os.Environ(), gcc.env...) + cmd.Stdin = gcc.stdin + fmt.Println("Running command:", strings.Join(cmd.Args, " ")) + output, err := cmd.CombinedOutput() + if err != nil { + return output, err + } + + return output, nil +} + +// Env returns the environment variables set for the GenericCommandContext +func (gcc *GenericCommandContext) Env() []string { + return gcc.env +} + +// Dir returns the directory set for the GenericCommandContext +func (gcc *GenericCommandContext) Dir() string { + return gcc.dir +} + +// Stdin returns the stdin set for the GenericCommandContext +func (gcc *GenericCommandContext) Stdin() io.Reader { + return gcc.stdin +} diff --git a/pkg/testutils/e2e/certmanager/helpers.go b/pkg/testutils/e2e/certmanager/helpers.go new file mode 100644 index 0000000..06dc432 --- /dev/null +++ b/pkg/testutils/e2e/certmanager/helpers.go @@ -0,0 +1,85 @@ +package certmanager + +import ( + "fmt" + "strconv" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" +) + +// InstallCertManagerBundle installs CertManager onto a Kubernetes cluster +func InstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) error { + url, err := getCertManagerURL(hasv1beta1CRs, kubectl) + if err != nil { + return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) + } + + _, err = kubectl.Apply(false, "-f", url, "--validate=false") + if err != nil { + return fmt.Errorf("encountered an error when applying the bundle: %w", err) + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + _, err = kubectl.Wait(false, "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m", + ) + if err != nil { + return fmt.Errorf("encountered an error when waiting for the webhook to be ready: %w", err) + } + + return nil +} + +// UninstallCertManagerBundle uninstalls CertManager from a Kubernetes cluster +func UninstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) error { + url, err := getCertManagerURL(hasv1beta1CRs, kubectl) + if err != nil { + return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) + } + + _, err = kubectl.Delete(false, "-f", url) + if err != nil { + return fmt.Errorf("encountered an error when deleting the bundle: %w", err) + } + + return nil +} + +// getCertManagerUrl is a helper function to determine the CertManager +// that should be installed on a cluster based on the Kubernetes version +func getCertManagerURL(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) (string, error) { + certmanagerVersionWithv1beta2CRs := "v0.11.0" + certmanagerLegacyVersion := "v1.0.4" + certmanagerVersion := "v1.5.3" + + certmanagerURLTmplLegacy := "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager-legacy.yaml" + certmanagerURLTmpl := "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" + // Return a URL for the manifest bundle with v1beta1 CRs. + + kubeVersion, err := kubectl.Version() + if err != nil { + return "", fmt.Errorf("encountered an error trying to get Kubernetes Version: %w", err) + } + serverMajor, err := strconv.ParseUint(kubeVersion.ServerVersion().Major(), 10, 64) + if err != nil { + return "", fmt.Errorf("encountered an error trying to parse Kubernetes Major Version: %w", err) + } + + serverMinor, err := strconv.ParseUint(kubeVersion.ServerVersion().Minor(), 10, 64) + if err != nil { + return "", fmt.Errorf("encountered an error trying to parse Kubernetes Minor Version: %w", err) + } + + if hasv1beta1CRs { + return fmt.Sprintf(certmanagerURLTmpl, certmanagerVersionWithv1beta2CRs), nil + } + + // Determine which URL to use for a manifest bundle with v1 CRs. + // The most up-to-date bundle uses v1 CRDs, which were introduced in k8s v1.16. + if serverMajor <= 1 && serverMinor < 16 { + return fmt.Sprintf(certmanagerURLTmplLegacy, certmanagerLegacyVersion), nil + } + return fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion), nil +} diff --git a/pkg/testutils/e2e/helpers.go b/pkg/testutils/e2e/helpers.go new file mode 100644 index 0000000..cbf38c7 --- /dev/null +++ b/pkg/testutils/e2e/helpers.go @@ -0,0 +1,52 @@ +package e2e + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" +) + +// CleanUpTestDir removes the test directory +func CleanUpTestDir(path string) error { + return os.RemoveAll(path) +} + +// CreateCustomResources will create the CRs that are specified in a Sample +func CreateCustomResources(sample sample.Sample, kubectl kubernetes.Kubectl) error { + for _, gvk := range sample.GVKs() { + sampleFile := filepath.Join( + "config", + "samples", + fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))) + + o, err := kubectl.Apply(true, "-f", sampleFile) + if err != nil { + return fmt.Errorf("encountered an error when applying CRD (%s): %w | OUTPUT: %s", sampleFile, err, o) + } + } + + return nil +} + +// AllowProjectBeMultiGroup will update the PROJECT file with the information to allow we scaffold +// apis with different groups. be available. +func AllowProjectBeMultiGroup(sample sample.Sample) error { + const multiGroup = `multigroup: true +` + projectBytes, err := ioutil.ReadFile(filepath.Join(sample.Dir(), "PROJECT")) + if err != nil { + return err + } + + projectBytes = append([]byte(multiGroup), projectBytes...) + err = ioutil.WriteFile(filepath.Join(sample.Dir(), "PROJECT"), projectBytes, 0644) + if err != nil { + return err + } + return nil +} diff --git a/pkg/testutils/e2e/kind/helpers.go b/pkg/testutils/e2e/kind/helpers.go new file mode 100644 index 0000000..be30e78 --- /dev/null +++ b/pkg/testutils/e2e/kind/helpers.go @@ -0,0 +1,35 @@ +package kind + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" +) + +// IsRunningOnKind checks if the Kubernetes cluster is a KinD cluster +func IsRunningOnKind(kubectl kubernetes.Kubectl) (bool, error) { + kubectx, err := kubectl.Command("config", "current-context") + if err != nil { + return false, err + } + return strings.Contains(kubectx, "kind"), nil +} + +// LoadImageToKindCluster will load an image onto a KinD cluster +func LoadImageToKindCluster(cc command.CommandContext, image string) error { + cluster := "kind" + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { + cluster = v + } + kindOptions := []string{"load", "docker-image", image, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) + o, err := cc.Run(cmd) + if err != nil { + return fmt.Errorf("encountered an error attempting to load image to KinD cluster: %w | OUTPUT: %s", err, o) + } + return nil +} diff --git a/pkg/testutils/e2e/metrics/helpers.go b/pkg/testutils/e2e/metrics/helpers.go new file mode 100644 index 0000000..4a8c1fb --- /dev/null +++ b/pkg/testutils/e2e/metrics/helpers.go @@ -0,0 +1,101 @@ +package metrics + +import ( + "encoding/base64" + "fmt" + "strings" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" + "github.com/operator-framework/ansible-operator-plugins/test/common" +) + +// GetMetrics creates a pod with the permissions to `curl` metrics. It will then return the output of the `curl` pod +func GetMetrics(sample sample.Sample, kubectl kubernetes.Kubectl, metricsClusterRoleBindingName string) string { + ginkgo.By("granting permissions to access the metrics and read the token") + out, err := kubectl.Command("create", "clusterrolebinding", metricsClusterRoleBindingName, + fmt.Sprintf("--clusterrole=%s-metrics-reader", sample.Name()), + fmt.Sprintf("--serviceaccount=%s:%s", kubectl.Namespace(), kubectl.ServiceAccount())) + fmt.Println("OUT --", out) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + // As of Kubernetes 1.24 a ServiceAccount no longer has a ServiceAccount token secret autogenerated. We have to create it manually here + ginkgo.By("Creating the ServiceAccount token") + secretFile, err := common.GetSASecret(kubectl.ServiceAccount(), sample.Dir()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Eventually(func() error { + out, err = kubectl.Apply(true, "-f", secretFile) + fmt.Println("OUT -- ", out) + return err + }, time.Minute, time.Second).Should(gomega.Succeed()) + + ginkgo.By("reading the metrics token") + // Filter token query by service account in case more than one exists in a namespace. + query := fmt.Sprintf(`{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="%s")].data.token}`, + kubectl.ServiceAccount(), + ) + out, err = kubectl.Get(true, "secrets") + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + fmt.Println("OUT --", out) + b64Token, err := kubectl.Get(true, "secrets", "-o=jsonpath="+query) + fmt.Println("OUT--", b64Token) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + token, err := base64.StdEncoding.DecodeString(strings.TrimSpace(b64Token)) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(len(token)).To(gomega.BeNumerically(">", 0)) + + ginkgo.By("creating a curl pod") + cmdOpts := []string{ + "run", "curl", "--image=curlimages/curl:7.68.0", "--restart=OnFailure", "--", + "curl", "-v", "-k", "-H", fmt.Sprintf(`Authorization: Bearer %s`, token), + fmt.Sprintf("https://%s-controller-manager-metrics-service.%s.svc:8443/metrics", sample.Name(), kubectl.Namespace()), + } + out, err = kubectl.CommandInNamespace(cmdOpts...) + fmt.Println("OUT --", out) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + ginkgo.By("validating that the curl pod is running as expected") + verifyCurlUp := func() error { + // Validate pod status + status, err := kubectl.Get( + true, + "pods", "curl", "-o", "jsonpath={.status.phase}") + if err != nil { + return err + } + if status != "Completed" && status != "Succeeded" { + return fmt.Errorf("curl pod in %s status", status) + } + return nil + } + gomega.Eventually(verifyCurlUp, 2*time.Minute, time.Second).Should(gomega.Succeed()) + + ginkgo.By("validating that the metrics endpoint is serving as expected") + var metricsOutput string + getCurlLogs := func() string { + metricsOutput, err = kubectl.Logs(true, "curl") + gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred()) + return metricsOutput + } + gomega.Eventually(getCurlLogs, time.Minute, time.Second).Should(gomega.ContainSubstring("< HTTP/2 200")) + + return metricsOutput +} + +// CleanUpMetrics with clean up the resources created to gather metrics information +func CleanUpMetrics(kubectl kubernetes.Kubectl, metricsClusterRoleBindingName string) error { + _, err := kubectl.Delete(true, "pod", "curl") + if err != nil { + return fmt.Errorf("encountered an error when deleting the metrics pod: %w", err) + } + + _, err = kubectl.Delete(false, "clusterrolebinding", metricsClusterRoleBindingName) + if err != nil { + return fmt.Errorf("encountered an error when deleting the metrics clusterrolebinding: %w", err) + } + + return nil +} diff --git a/pkg/testutils/e2e/olm/helpers.go b/pkg/testutils/e2e/olm/helpers.go new file mode 100644 index 0000000..4713184 --- /dev/null +++ b/pkg/testutils/e2e/olm/helpers.go @@ -0,0 +1,173 @@ +package olm + +import ( + "fmt" + "io/ioutil" + "os/exec" + "path/filepath" + "regexp" + + "github.com/operator-framework/ansible-operator-plugins/internal/annotations/metrics" + "github.com/operator-framework/ansible-operator-plugins/internal/util/projutil" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" + kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" +) + +const ( + OlmVersionForTestSuite = "0.20.0" +) + +// AddPackagemanifestsTarget will append the packagemanifests target to the makefile +// in order to test the steps described in the docs. +// More info: https://v1-0-x.sdk.operatorframework.io/docs/olm-integration/generation/#package-manifests-formats +func AddPackagemanifestsTarget(sample sample.Sample, operatorType projutil.OperatorType) error { + var makefilePackagemanifestsFragment = `# Options for "packagemanifests". +ifneq ($(origin FROM_VERSION), undefined) +PKG_FROM_VERSION := --from-version=$(FROM_VERSION) +endif +ifneq ($(origin CHANNEL), undefined) +PKG_CHANNELS := --channel=$(CHANNEL) +endif +ifeq ($(IS_CHANNEL_DEFAULT), 1) +PKG_IS_DEFAULT_CHANNEL := --default-channel +endif +PKG_MAN_OPTS ?= $(PKG_FROM_VERSION) $(PKG_CHANNELS) $(PKG_IS_DEFAULT_CHANNEL) + +# Generate package manifests. +packagemanifests: kustomize %s + operator-sdk generate kustomize manifests -q --interactive=false + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/manifests | operator-sdk generate packagemanifests -q --version $(VERSION) $(PKG_MAN_OPTS) +` + + makefileBytes, err := ioutil.ReadFile(filepath.Join(sample.Dir(), "Makefile")) + if err != nil { + return err + } + + // add the manifests target when is a Go project. + replaceTarget := "" + if operatorType == projutil.OperatorTypeGo { + replaceTarget = "manifests" + } + makefilePackagemanifestsFragment = fmt.Sprintf(makefilePackagemanifestsFragment, replaceTarget) + + // update makefile by adding the packagemanifests target + makefileBytes = append([]byte(makefilePackagemanifestsFragment), makefileBytes...) + err = ioutil.WriteFile(filepath.Join(sample.Dir(), "Makefile"), makefileBytes, 0644) + if err != nil { + return err + } + return nil +} + +// DisableOLMBundleInterativeMode will update the Makefile to disable the interactive mode +func DisableManifestsInteractiveMode(sample sample.Sample) error { + // Todo: check if we cannot improve it since the replace/content will exists in the + // pkgmanifest target if it be scaffolded before this call + content := "$(OPERATOR_SDK) generate kustomize manifests" + replace := content + " --interactive=false" + return kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "Makefile"), content, replace) +} + +// GenerateBundle runs all commands to create an operator bundle. +func GenerateBundle(sample sample.Sample, image string) error { + if err := DisableManifestsInteractiveMode(sample); err != nil { + return err + } + + cmd := exec.Command("make", "bundle", "IMG="+image) + if _, err := sample.CommandContext().Run(cmd, sample.Name()); err != nil { + return err + } + + return nil +} + +// InstallOLM runs 'operator-sdk olm install' for specific version +// and returns any errors emitted by that command. +func InstallOLMVersion(sample sample.Sample, version string) error { + cmd := exec.Command(sample.Binary(), "olm", "install", "--version", version, "--timeout", "4m") + _, err := sample.CommandContext().Run(cmd) + return err +} + +// InstallOLM runs 'operator-sdk olm uninstall' and logs any errors emitted by that command. +func UninstallOLM(sample sample.Sample) { + cmd := exec.Command(sample.Binary(), "olm", "uninstall") + if _, err := sample.CommandContext().Run(cmd); err != nil { + fmt.Println("warning: error when uninstalling OLM:", err) + } +} + +// StripBundleAnnotations removes all annotations applied to bundle manifests and metadata +// by operator-sdk/internal/annotations/metrics annotators. Doing so decouples samples +// from which operator-sdk version they were build with, as this information is already +// available in git history. +func StripBundleAnnotations(sample sample.Sample) (err error) { + // Remove metadata labels. + metadataAnnotations := metrics.MakeBundleMetadataLabels("") + metadataFiles := []string{ + filepath.Join(sample.Dir(), "bundle", "metadata", "annotations.yaml"), + filepath.Join(sample.Dir(), "bundle.Dockerfile"), + } + if err = removeAllAnnotationLines(metadataAnnotations, metadataFiles); err != nil { + return err + } + + // Remove manifests annotations. + manifestsAnnotations := metrics.MakeBundleObjectAnnotations("") + manifestsFiles := []string{ + filepath.Join(sample.Dir(), "bundle", "manifests", sample.Name()+".clusterserviceversion.yaml"), + filepath.Join(sample.Dir(), "config", "manifests", "bases", sample.Name()+".clusterserviceversion.yaml"), + } + if err = removeAllAnnotationLines(manifestsAnnotations, manifestsFiles); err != nil { + return err + } + + return nil +} + +// removeAllAnnotationLines removes each line containing a key in annotations from all files at filePaths. +func removeAllAnnotationLines(annotations map[string]string, filePaths []string) error { + var annotationREs []*regexp.Regexp + for annotation := range annotations { + re, err := regexp.Compile(".+" + regexp.QuoteMeta(annotation) + ".+\n") + if err != nil { + return fmt.Errorf("compiling annotation regexp: %v", err) + } + annotationREs = append(annotationREs, re) + } + + for _, file := range filePaths { + b, err := ioutil.ReadFile(file) + if err != nil { + return err + } + for _, re := range annotationREs { + b = re.ReplaceAll(b, []byte{}) + } + err = ioutil.WriteFile(file, b, 0644) + if err != nil { + return err + } + } + return nil +} + +// BuildBundleImage is a helper function to run `make bundle-build BUNDLE_IMAGE=` +func BuildBundleImage(sample sample.Sample, bundleImage string) error { + cmd := exec.Command("make", "bundle-build", "BUNDLE_IMG="+bundleImage) + _, err := sample.CommandContext().Run(cmd, sample.Name()) + return err +} + +// GeneratePackageManifests is a helper function to run `make packagemanifests IMG=` +func GeneratePackageManifests(sample sample.Sample, image string) error { + cmd := exec.Command("make", "packagemanifests", "IMG="+image) + o, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + return fmt.Errorf("encountered an error when generating the packagemanifests: %w | OUTPUT: %s", err, o) + } + return nil +} diff --git a/pkg/testutils/e2e/operator/helpers.go b/pkg/testutils/e2e/operator/helpers.go new file mode 100644 index 0000000..0e3c4a0 --- /dev/null +++ b/pkg/testutils/e2e/operator/helpers.go @@ -0,0 +1,99 @@ +package operator + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" + kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" +) + +// BuildOperatorImage will build an operator image by running `make docker-build IMG=` +func BuildOperatorImage(sample sample.Sample, image string) error { + cmd := exec.Command("make", "docker-build", "IMG="+image) + _, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + return fmt.Errorf("encountered an error when building the operator image: %w", err) + } + + return nil +} + +// DeployOperator will deploy an operator onto a Kubernetes cluster by running `make deploy IMG=` +func DeployOperator(sample sample.Sample, image string) error { + cmd := exec.Command("make", "deploy", "IMG="+image) + _, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + return fmt.Errorf("encountered an error when deploying the operator: %w", err) + } + + return nil +} + +// UndeployOperator will clean up an operator from a Kubernetes cluster by running `make undeploy` +func UndeployOperator(sample sample.Sample) error { + cmd := exec.Command("make", "undeploy") + _, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + fmt.Errorf("encountered an error when undeploying the operator: %w", err) + } + + return nil +} + +// EnsureOperatorRunning makes sure that an operator is running with with expected number of pods, +// the pod name contains a specific substring and is running in the specified control-plane +func EnsureOperatorRunning(kubectl kubernetes.Kubectl, expectedNumPods int, podNameShouldContain string, controlPlane string) (string, error) { + // Get the controller-manager pod name + podOutput, err := kubectl.Get( + true, + "pods", "-l", "control-plane="+controlPlane, + "-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}") + if err != nil { + return "", fmt.Errorf("could not get pods: %v", err) + } + podNames := kbutil.GetNonEmptyLines(podOutput) + if len(podNames) != expectedNumPods { + return "", fmt.Errorf("expecting %d pod(s), have %d", expectedNumPods, len(podNames)) + } + controllerPodName := podNames[0] + if !strings.Contains(controllerPodName, podNameShouldContain) { + return "", fmt.Errorf("expecting pod name %q to contain %q", controllerPodName, podNameShouldContain) + } + + // Ensure the controller-manager Pod is running. + status, err := kubectl.Get( + true, + "pods", controllerPodName, "-o", "jsonpath={.status.phase}") + if err != nil { + return "", fmt.Errorf("failed to get pod status for %q: %v", controllerPodName, err) + } + if status != "Running" { + return "", fmt.Errorf("controller pod in %s status", status) + } + return controllerPodName, nil +} + +// InstallCRDs will install the CRDs for a sample onto the cluster +func InstallCRDs(sample sample.Sample) error { + cmd := exec.Command("make", "install") + o, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + return fmt.Errorf("encountered an error when installing the CRDs: %w | OUTPUT: %s", err, o) + } + return nil +} + +// UninstallCRDs will uninstall the CRDs for a sample from the cluster +func UninstallCRDs(sample sample.Sample) error { + cmd := exec.Command("make", "uninstall") + o, err := sample.CommandContext().Run(cmd, sample.Name()) + if err != nil { + return fmt.Errorf("encountered an error uninstalling the CRDs: %w | OUTPUT: %s", err, o) + } + + return nil +} diff --git a/pkg/testutils/e2e/prometheus/helpers.go b/pkg/testutils/e2e/prometheus/helpers.go new file mode 100644 index 0000000..b8f1273 --- /dev/null +++ b/pkg/testutils/e2e/prometheus/helpers.go @@ -0,0 +1,71 @@ +package prometheus + +import ( + "fmt" + "strconv" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" +) + +// InstallPrometheusOperator will install the Prometheus operator onto a Kubernetes cluster +func InstallPrometheusOperator(kubectl kubernetes.Kubectl) error { + url, err := getPrometheusOperatorUrl(kubectl) + if err != nil { + return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) + } + + _, err = kubectl.Apply(false, "-f", url) + if err != nil { + return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) + } + + return nil +} + +// UninstallPrometheusOperator will uninstall a Prometheus operator from a Kubernetes cluster +func UninstallPrometheusOperator(kubectl kubernetes.Kubectl) error { + url, err := getPrometheusOperatorUrl(kubectl) + if err != nil { + return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) + } + _, err = kubectl.Apply(false, "-f", url) + if err != nil { + return fmt.Errorf("encountered an error when deleting the bundle: %w", err) + } + + return nil +} + +// getPrometheusOperatorUrl is a helper function to determine the Prometheus +// operator that should be installed on a cluster based on the Kubernetes version +func getPrometheusOperatorUrl(kubectl kubernetes.Kubectl) (string, error) { + prometheusOperatorLegacyVersion := "0.33" + prometheusOperatorLegacyURL := "https://raw.githubusercontent.com/coreos/prometheus-operator/release-%s/bundle.yaml" + prometheusOperatorVersion := "0.51" + prometheusOperatorURL := "https://raw.githubusercontent.com/prometheus-operator/" + + "prometheus-operator/release-%s/bundle.yaml" + + var url string + + kubeVersion, err := kubectl.Version() + if err != nil { + return "", fmt.Errorf("encountered an error trying to get Kubernetes Version: %w", err) + } + serverMajor, err := strconv.ParseUint(kubeVersion.ServerVersion().Major(), 10, 64) + if err != nil { + return "", fmt.Errorf("encountered an error trying to parse Kubernetes Major Version: %w", err) + } + + serverMinor, err := strconv.ParseUint(kubeVersion.ServerVersion().Minor(), 10, 64) + if err != nil { + return "", fmt.Errorf("encountered an error trying to parse Kubernetes Minor Version: %w", err) + } + + if serverMajor <= 1 && serverMinor < 16 { + url = fmt.Sprintf(prometheusOperatorLegacyURL, prometheusOperatorLegacyVersion) + } else { + url = fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + } + + return url, nil +} diff --git a/pkg/testutils/kubernetes/kubectl.go b/pkg/testutils/kubernetes/kubectl.go new file mode 100644 index 0000000..f3b195d --- /dev/null +++ b/pkg/testutils/kubernetes/kubectl.go @@ -0,0 +1,173 @@ +package kubernetes + +import ( + "encoding/json" + "fmt" + "os/exec" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" +) + +// Kubectl represents a command line utility that is used for interacting with a Kubernetes cluster +type Kubectl interface { + // CommandContext returns the CommandContext that is being used by a Kubectl implementation + CommandContext() command.CommandContext + // Namespace returns the namespace that a Kubectl implementation is configured to use + Namespace() string + // ServiceAccount returns the service account that a Kubectl implementation is configured to use + ServiceAccount() string + + // Command is used to run any command prefaced by the tool name. i.e `kubectl ...` + Command(options ...string) (string, error) + // CommandInNamespace is used to run any command in a namespace, prefaced by the tool name. i.e `kubectl -n namespace ...` + CommandInNamespace(options ...string) (string, error) + // Apply is used to run the `apply` subcommand, and can be run in the specified namespace. i.e `kubectl apply ...` or `kubectl apply -n namespace ...`. + Apply(inNamespace bool, options ...string) (string, error) + // Get is used to run the `get` subcommand, and can be run in the specified namespace. i.e `kubectl get ...` or `kubectl get -n namespace ...`. + Get(inNamespace bool, options ...string) (string, error) + // Delete is used to run the `delete` subcommand, and can be run in the specified namespace. i.e `kubectl delete ...` or `kubectl delete -n namespace ...`. + Delete(inNamespace bool, options ...string) (string, error) + // Logs is used to run the `logs` subcommand, and can be run in the specified namespace. i.e `kubectl logs ...` or `kubectl logs -n namespace ...`. + Logs(inNamespace bool, options ...string) (string, error) + // Wait is used to run the `wait` subcommand, and can be run in the specified namespace. i.e `kubectl wait ...` or `kubectl wait -n namespace ...`. + Wait(inNamespace bool, options ...string) (string, error) + // Version will return the KubernetesVersion that can be retrieved from running `kubectl version` + Version() (KubernetesVersion, error) +} + +// KubectlUtil is an implementation of the Kubectl interface that uses the `kubectl` command line utility +type KubectlUtil struct { + commandContext command.CommandContext + namespace string + serviceAccount string +} + +// KubectlUtilOptions are functions used to configure a KubectlUtil +type KubectlUtilOptions func(ku *KubectlUtil) + +// WithCommandContext configures the CommandContext used by a KubectlUtil +func WithCommandContext(cc command.CommandContext) KubectlUtilOptions { + return func(ku *KubectlUtil) { + ku.commandContext = cc + } +} + +// WithNamespace configures the namespace used by a KubectlUtil when commands are run namespaced +func WithNamespace(ns string) KubectlUtilOptions { + return func(ku *KubectlUtil) { + ku.namespace = ns + } +} + +// WithServiceAccount configures the service account used by a KubectlUtil +func WithServiceAccount(sa string) KubectlUtilOptions { + return func(ku *KubectlUtil) { + ku.serviceAccount = sa + } +} + +// NewKubectlUtil creates a new KubectlUtil that can be configured with KubectlUtilOptions functions +func NewKubectlUtil(opts ...KubectlUtilOptions) *KubectlUtil { + ku := &KubectlUtil{ + commandContext: command.NewGenericCommandContext(), + namespace: "test-ns", + serviceAccount: "test-sa", + } + + for _, opt := range opts { + opt(ku) + } + + return ku +} + +// CommandContext returns the CommandContext that is being used by a KubectlUtil +func (ku *KubectlUtil) CommandContext() command.CommandContext { + return ku.commandContext +} + +// Namespace returns the namespace that a KubectlUtil is configured to use +func (ku *KubectlUtil) Namespace() string { + return ku.namespace +} + +// ServiceAccount returns the service account that a KubectlUtil is configured to use +func (ku *KubectlUtil) ServiceAccount() string { + return ku.serviceAccount +} + +// Command is used to run any command prefaced by `kubectl`. i.e `kubectl ...` +func (ku *KubectlUtil) Command(options ...string) (string, error) { + cmd := exec.Command("kubectl", options...) + output, err := ku.commandContext.Run(cmd) + return string(output), err +} + +// CommandInNamespace is used to run any command in a namespace, prefaced by `kubectl`. i.e `kubectl -n namespace ...` +func (ku *KubectlUtil) CommandInNamespace(options ...string) (string, error) { + opts := append([]string{"-n", ku.namespace}, options...) + return ku.Command(opts...) +} + +// Apply is used to run the `kubectl apply` subcommand, and can be run in the specified namespace. i.e `kubectl apply ...` or `kubectl apply -n namespace ...`. +func (ku *KubectlUtil) Apply(inNamespace bool, options ...string) (string, error) { + return ku.prefixCommand("apply", inNamespace, options...) +} + +// Get is used to run the `kubectl get` subcommand, and can be run in the specified namespace. i.e `kubectl get ...` or `kubectl get -n namespace ...`. +func (ku *KubectlUtil) Get(inNamespace bool, options ...string) (string, error) { + return ku.prefixCommand("get", inNamespace, options...) +} + +// Delete is used to run the `kubectl delete` subcommand, and can be run in the specified namespace. i.e `kubectl delete ...` or `kubectl delete -n namespace ...`. +func (ku *KubectlUtil) Delete(inNamespace bool, options ...string) (string, error) { + return ku.prefixCommand("delete", inNamespace, options...) +} + +// Logs is used to run the `kubectl logs` subcommand, and can be run in the specified namespace. i.e `kubectl logs ...` or `kubectl logs -n namespace ...`. +func (ku *KubectlUtil) Logs(inNamespace bool, options ...string) (string, error) { + return ku.prefixCommand("logs", inNamespace, options...) +} + +// Wait is used to run the `kubectl wait` subcommand, and can be run in the specified namespace. i.e `kubectl wait ...` or `kubectl wait -n namespace ...`. +func (ku *KubectlUtil) Wait(inNamespace bool, options ...string) (string, error) { + return ku.prefixCommand("wait", inNamespace, options...) +} + +// Version is used to run the `kubectl version` subcommand, and returns the KubernetesVersion that is parsed from its output +func (ku *KubectlUtil) Version() (KubernetesVersion, error) { + out, err := ku.Command("version", "-o", "json") + if err != nil { + return nil, err + } + + var versions map[string]json.RawMessage + + err = json.Unmarshal([]byte(out), &versions) + if err != nil { + return nil, fmt.Errorf("error unmarshalling json: %w", err) + } + + clientVersion, err := NewKubeVersionInfo(string(versions["clientVersion"])) + if err != nil { + return nil, fmt.Errorf("error getting client version: %w", err) + } + + serverVersion, err := NewKubeVersionInfo(string(versions["serverVersion"])) + if err != nil { + return nil, fmt.Errorf("error getting server version: %w", err) + } + + return NewKubeVersion(WithClientVersion(clientVersion), WithServerVersion(serverVersion)), nil +} + +// prefixCommand is a helper function that will prefix the subcommand and its options with `kubectl` or `kubectl -n namespace`. +func (ku *KubectlUtil) prefixCommand(subcommand string, inNamespace bool, options ...string) (string, error) { + opts := append([]string{subcommand}, options...) + + if inNamespace { + return ku.CommandInNamespace(opts...) + } + + return ku.Command(opts...) +} diff --git a/pkg/testutils/kubernetes/version.go b/pkg/testutils/kubernetes/version.go new file mode 100644 index 0000000..bc0457b --- /dev/null +++ b/pkg/testutils/kubernetes/version.go @@ -0,0 +1,118 @@ +package kubernetes + +import ( + "encoding/json" + "strings" +) + +// VersionInfo represents the version information that is returned when running something like `kubectl version` +type VersionInfo interface { + // Major returns the string representation of the Major version + Major() string + // Minor returns the string representatiion of the Minor version + Minor() string + // GitVersion returns the string representation of the GitVersion + GitVersion() string +} + +// KubernetesVersion represents the JSON response that is returned when running something like `kubectl version` +type KubernetesVersion interface { + // ClientVersion returns the VersionInfo for the client + ClientVersion() VersionInfo + // ServerVersion returns the VersionInfo for the server + ServerVersion() VersionInfo +} + +// kubeVersionInfoJson is a struct that allows for easier parsing of the JSON version information from something like `kubectl version` +type kubeVersionInfoJson struct { + Major string `json:"major"` + Minor string `json:"minor"` + GitVersion string `json:"gitVersion"` +} + +// KubeVersionInfo is an implementation of the VersionInfo interface +type KubeVersionInfo struct { + kubeVersionInfoJson +} + +// NewKubeVersionInfo will return a KubeVersionInfo from a given JSON string +func NewKubeVersionInfo(out string) (*KubeVersionInfo, error) { + kvi := &KubeVersionInfo{} + dec := json.NewDecoder(strings.NewReader(out)) + if err := dec.Decode(&kvi.kubeVersionInfoJson); err != nil { + return nil, err + } + + return kvi, nil +} + +// Major returns the string representation of the Major version +func (kvi *KubeVersionInfo) Major() string { + return kvi.kubeVersionInfoJson.Major +} + +// Minor returns the string representatiion of the Minor version +func (kvi *KubeVersionInfo) Minor() string { + return kvi.kubeVersionInfoJson.Minor +} + +// GitVersion returns the string representation of the GitVersion +func (kvi *KubeVersionInfo) GitVersion() string { + return kvi.kubeVersionInfoJson.GitVersion +} + +// KubeVersion is an implementation of the KubernetesVersion interface +type KubeVersion struct { + clientVersion KubeVersionInfo `json:"clientVersion,omitempty"` + serverVersion KubeVersionInfo `json:"serverVersion,omitempty"` +} + +// KubeVersionOptions is for configuring a KubeVersion +type KubeVersionOptions func(kv *KubeVersion) + +// WithClientVersion will set the ClientVersion for a KubeVersion +func WithClientVersion(clientVersion VersionInfo) KubeVersionOptions { + return func(kv *KubeVersion) { + kv.clientVersion = KubeVersionInfo{ + kubeVersionInfoJson: kubeVersionInfoJson{ + Major: clientVersion.Major(), + Minor: clientVersion.Minor(), + GitVersion: clientVersion.GitVersion(), + }, + } + } +} + +// WithServerVersion configures the ServerVersion for a KubeVersion +func WithServerVersion(serverVersion VersionInfo) KubeVersionOptions { + return func(kv *KubeVersion) { + kv.serverVersion = KubeVersionInfo{ + kubeVersionInfoJson: kubeVersionInfoJson{ + Major: serverVersion.Major(), + Minor: serverVersion.Minor(), + GitVersion: serverVersion.GitVersion(), + }, + } + } +} + +// NewKubeVersion returns a new KubeVersion and can be configured via KubeVersionOptions functions +func NewKubeVersion(opts ...KubeVersionOptions) *KubeVersion { + kv := &KubeVersion{} + + for _, opt := range opts { + opt(kv) + } + + return kv +} + +// ClientVersion returns the VersionInfo for the client +func (kv *KubeVersion) ClientVersion() VersionInfo { + return &kv.clientVersion +} + +// ServerVersion returns the VersionInfo for the server +func (kv *KubeVersion) ServerVersion() VersionInfo { + return &kv.serverVersion +} diff --git a/pkg/testutils/sample/cli/cli.go b/pkg/testutils/sample/cli/cli.go new file mode 100644 index 0000000..bddf147 --- /dev/null +++ b/pkg/testutils/sample/cli/cli.go @@ -0,0 +1,330 @@ +package cli + +import ( + "fmt" + "os" + "strings" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/kubebuilder/v3/pkg/cli" + cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" + "sigs.k8s.io/kubebuilder/v3/pkg/plugin" + kustomizev2Alpha "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v2-alpha" + "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang" + golangv4 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v4" +) + +// CliSample is a generalized object that implements the Sample interface. It is meant +// to be as simple and versatile to make the process of generating test samples easier +// TODO: Consider making it easier to create custom cli flags on a per GVK basis for the `create api` and `create webhook` subcommands +type CliSample struct { + domain string + repo string + gvks []schema.GroupVersionKind + commandContext command.CommandContext + name string + plugins []string + cli *cli.CLI + + initOptions []string + apiOptions []string + webhookOptions []string +} + +// CliSampleOption is a function that modifies the values in a CliSample to be used when creating a new CliSample +type CliSampleOption func(gs *CliSample) + +// WithDomain sets the domain to be used during scaffold execution +func WithDomain(domain string) CliSampleOption { + return func(gs *CliSample) { + gs.domain = domain + } +} + +// WithRepository sets the repository to be used during scaffold execution +func WithRepository(repo string) CliSampleOption { + return func(gs *CliSample) { + gs.repo = repo + } +} + +// WithGvk sets the GroupVersionKind to be used during scaffold execution +func WithGvk(gvks ...schema.GroupVersionKind) CliSampleOption { + return func(gs *CliSample) { + gs.gvks = make([]schema.GroupVersionKind, len(gvks)) + copy(gs.gvks, gvks) + } +} + +// WithName sets the name of the sample that is scaffolded +func WithName(name string) CliSampleOption { + return func(gs *CliSample) { + gs.name = name + } +} + +// WithCommandContext sets the CommandContext that is used to execute scaffold commands +func WithCommandContext(commandContext command.CommandContext) CliSampleOption { + return func(gs *CliSample) { + gs.commandContext = commandContext + } +} + +// WithPlugins sets the plugins that should be used during scaffolding +func WithPlugins(plugins ...string) CliSampleOption { + return func(gs *CliSample) { + gs.plugins = make([]string, len(plugins)) + copy(gs.plugins, plugins) + } +} + +// WithExtraInitOptions sets any additional options that should be passed into an init subcommand +func WithExtraInitOptions(options ...string) CliSampleOption { + return func(gs *CliSample) { + gs.initOptions = make([]string, len(options)) + copy(gs.initOptions, options) + } +} + +// WithExtraApiOptions sets any additional options that should be passed into a create api subcommand +func WithExtraApiOptions(options ...string) CliSampleOption { + return func(gs *CliSample) { + gs.apiOptions = make([]string, len(options)) + copy(gs.apiOptions, options) + } +} + +// WithExtraWebhookOptions sets any additional options that should be passed into a create webhook subcommand +func WithExtraWebhookOptions(options ...string) CliSampleOption { + return func(gs *CliSample) { + gs.webhookOptions = make([]string, len(options)) + copy(gs.webhookOptions, options) + } +} + +// WithCLI sets the CLI tool that this sample should use for generation +func WithCLI(c *cli.CLI) CliSampleOption { + return func(gs *CliSample) { + gs.cli = c + } +} + +// NewCliSample will return a new CliSample object. The values used in the CliSample can be modified using CliSampleOption functions +func NewCliSample(opts ...CliSampleOption) (*CliSample, error) { + // Create a very basic default CLI with a go/v3 plugin + gov4Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, golangv4.Plugin{}.Version(), + kustomizev2Alpha.Plugin{}, + golangv4.Plugin{}, + ) + + c, err := cli.New( + cli.WithCommandName("cli"), + cli.WithVersion("v0.0.0"), + cli.WithPlugins( + gov4Bundle, + ), + cli.WithDefaultPlugins(cfgv3.Version, gov4Bundle), + cli.WithDefaultProjectVersion(cfgv3.Version), + cli.WithCompletion(), + ) + if err != nil { + return nil, fmt.Errorf("encountered an error creating a new CliSample: %w", err) + } + + gs := &CliSample{ + domain: "example.com", + name: "cli-sample", + gvks: []schema.GroupVersionKind{ + { + Group: "sample", + Version: "v1", + Kind: "Cli", + }, + }, + repo: "", + commandContext: command.NewGenericCommandContext(), + plugins: []string{"go/v3"}, + cli: c, + } + + for _, opt := range opts { + opt(gs) + } + + return gs, nil +} + +// CommandContext returns the CommandContext that the CliSample is using +func (gs *CliSample) CommandContext() command.CommandContext { + return gs.commandContext +} + +// Name returns the name of the CliSample +func (gs *CliSample) Name() string { + return gs.name +} + +// GVKs returns the list of GVKs that is used for creating apis and webhooks +func (gs *CliSample) GVKs() []schema.GroupVersionKind { + return gs.gvks +} + +// Dir returns the directory the sample is created in +func (gs *CliSample) Dir() string { + return gs.commandContext.Dir() + "/" + gs.name +} + +// Binary returns the binary used when creating the sample +func (gs *CliSample) Binary() string { + return "custom-cli" +} + +// Domain returns the domain of the CliSample +func (gs *CliSample) Domain() string { + return gs.domain +} + +// GenerateInit runs the `init` subcommand of the binary provided +func (gs *CliSample) GenerateInit() error { + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("encountered an error getting the current working directory: %w", err) + } + // defer returning to the original working directory + defer func() { os.Chdir(cwd) }() + // Change directory to the context specified + err = os.MkdirAll(gs.Dir(), 0777) + if err != nil { + return fmt.Errorf("encountered an error creating context directory: %w", err) + } + err = os.Chdir(gs.Dir()) + if err != nil { + return fmt.Errorf("encountered an error switching to context directory: %w", err) + } + + // Cobra's Execute command by default uses os.Args[1:] so we need to add an extra + // arg to take place of os.Args[0] + args := []string{ + "cli", + "init", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--domain", + gs.domain, + } + + if gs.repo != "" { + args = append(args, "--repo", gs.repo) + } + + args = append(args, gs.initOptions...) + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = args + + err = gs.cli.Run() + if err != nil { + return fmt.Errorf("encountered an error when running `init` subcommand for the cli sample: %w", err) + } + + return nil +} + +// GenerateApi runs the `create api` subcommand of the binary provided +func (gs *CliSample) GenerateApi() error { + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("encountered an error getting the current working directory: %w", err) + } + // defer returning to the original working directory + defer func() { os.Chdir(cwd) }() + // Change directory to the context specified + err = os.MkdirAll(gs.Dir(), os.ModeDir) + if err != nil { + return fmt.Errorf("encountered an error creating context directory: %w", err) + } + err = os.Chdir(gs.Dir()) + if err != nil { + return fmt.Errorf("encountered an error switching to context directory: %w", err) + } + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + for _, gvk := range gs.gvks { + args := []string{ + "cli", + "create", + "api", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--group", + gvk.Group, + "--version", + gvk.Version, + "--kind", + gvk.Kind, + } + + args = append(args, gs.apiOptions...) + + os.Args = args + + err := gs.cli.Run() + if err != nil { + return fmt.Errorf("encountered an error when running `create api` subcommand for the cli sample: %w", err) + } + } + + return nil +} + +// GenerateWebhook runs the `create webhook` subcommand of the binary provided +func (gs *CliSample) GenerateWebhook() error { + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("encountered an error getting the current working directory: %w", err) + } + // defer returning to the original working directory + defer func() { os.Chdir(cwd) }() + err = os.MkdirAll(gs.Dir(), os.ModeDir) + if err != nil { + return fmt.Errorf("encountered an error creating context directory: %w", err) + } + // Change directory to the context specified + err = os.Chdir(gs.Dir()) + if err != nil { + return fmt.Errorf("encountered an error switching to context directory: %w", err) + } + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + for _, gvk := range gs.gvks { + args := []string{ + "cli", + "create", + "webhook", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--group", + gvk.Group, + "--version", + gvk.Version, + "--kind", + gvk.Kind, + } + + args = append(args, gs.webhookOptions...) + + os.Args = args + + err := gs.cli.Run() + if err != nil { + return fmt.Errorf("encountered an error when running `create api` subcommand for the cli sample: %w", err) + } + } + + return nil +} diff --git a/pkg/testutils/sample/generator.go b/pkg/testutils/sample/generator.go new file mode 100644 index 0000000..2c1f0ad --- /dev/null +++ b/pkg/testutils/sample/generator.go @@ -0,0 +1,145 @@ +package sample + +import "fmt" + +// Generator is a utility that can be used to generate multiple samples at a time. +// A Generator can be configured to run only specific subcommands for the samples via GeneratorOptions functions +type Generator struct { + init bool + api bool + webhook bool + preInit GeneratorHook + postInit GeneratorHook + preApi GeneratorHook + postApi GeneratorHook + preWebhook GeneratorHook + postWebhook GeneratorHook +} + +// GeneratorHook is a function that takes in a sample and represents a function that gets called in a Pre/Post hook for different stages of sample generation +type GeneratorHook func(Sample) + +// GeneratorOptions is a type of function that is used to configure a Generator +type GeneratorOptions func(g *Generator) + +// WithNoInit will configure a Generator to not run the GenerateInit function of a Sample +func WithNoInit() GeneratorOptions { + return func(g *Generator) { + g.init = false + } +} + +// WithNoApi will configure a Generator to not run the GenerateApi function of a Sample +func WithNoApi() GeneratorOptions { + return func(g *Generator) { + g.api = false + } +} + +// WithNoWebhook will configure a Generator to not run the GenerateWebhook function of a Sample +func WithNoWebhook() GeneratorOptions { + return func(g *Generator) { + g.webhook = false + } +} + +// WithPreInitHook will configure a Generator to run the given GeneratorHook before executing the GenerateInit function of a Sample +func WithPreInitHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.preInit = hook + } +} + +// WithPostInitHook will configure a Generator to run the given GeneratorHook after executing the GenerateInit function of a Sample +func WithPostInitHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.postInit = hook + } +} + +// WithPreApiHook will configure a Generator to run the given GeneratorHook before executing the GenerateApi function of a Sample +func WithPreApiHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.preApi = hook + } +} + +// WithPostApiHook will configure a Generator to run the given GeneratorHook after executing the GenerateApi function of a Sample +func WithPostApiHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.postApi = hook + } +} + +// WithPreWebhookHook will configure a Generator to run the given GeneratorHook before executing the GenerateWebhook function of a Sample +func WithPreWebhookHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.preWebhook = hook + } +} + +// WithPostWebhookHook will configure a Generator to run the given GeneratorHook after executing the GenerateWebhook function of a Sample +func WithPostWebhookHook(hook GeneratorHook) GeneratorOptions { + return func(g *Generator) { + g.postWebhook = hook + } +} + +// NewGenerator creates a new Generator. The returned Generator can be configured with GeneratorOptions functions. +// By default the Generator that is returned will be set to run all Generate functions of a Sample (GenerateInit, GenerateApi, GenerateWebhook) +func NewGenerator(opts ...GeneratorOptions) *Generator { + // create a default GeneratorHook that does nothing so we dont get nil pointer errors + defaultHook := func(s Sample) {} + g := &Generator{ + init: true, + api: true, + webhook: true, + preInit: defaultHook, + postInit: defaultHook, + preApi: defaultHook, + postApi: defaultHook, + preWebhook: defaultHook, + postWebhook: defaultHook, + } + + for _, opt := range opts { + opt(g) + } + + return g +} + +// GenerateSamples will perform the generation logic for a list of Samples based on the Generator configuration +func (g *Generator) GenerateSamples(samples ...Sample) error { + for _, sample := range samples { + fmt.Println("scaffolding sample: ", sample.Name()) + if g.init { + g.preInit(sample) + err := sample.GenerateInit() + if err != nil { + return fmt.Errorf("error in init generation for sample %s: %w", sample.Name(), err) + } + g.postInit(sample) + } + + if g.api { + g.preApi(sample) + err := sample.GenerateApi() + if err != nil { + return fmt.Errorf("error in api generation for sample %s: %w", sample.Name(), err) + } + g.postApi(sample) + } + + if g.webhook { + g.preWebhook(sample) + err := sample.GenerateWebhook() + if err != nil { + return fmt.Errorf("error in webhook generation for sample %s: %w", sample.Name(), err) + } + g.postWebhook(sample) + } + } + + return nil +} diff --git a/pkg/testutils/sample/sample.go b/pkg/testutils/sample/sample.go new file mode 100644 index 0000000..cd75780 --- /dev/null +++ b/pkg/testutils/sample/sample.go @@ -0,0 +1,267 @@ +package sample + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Sample represents a sample project that can be created and used for testing +type Sample interface { + // CommandContext returns the CommandContext that the Sample is using + CommandContext() command.CommandContext + // Name returns the name of the Sample + Name() string + // GVKs return an array of GVKs that are used when generating the apis and webhooks for the Sample + GVKs() []schema.GroupVersionKind + // Domain returs the domain of the sample + Domain() string + // Dir returns the directory the sample is created in + Dir() string + // Binary returns the binary that is used when creating a sample + Binary() string + // GenerateInit scaffolds using the `init` subcommand + GenerateInit() error + // GenerateApi scaffolds using the `create api` subcommand + GenerateApi() error + // GenerateWebhook scaffolds using the `create webhook` subcommand + GenerateWebhook() error +} + +// GenericSample is a generalized object that implements the Sample interface. It is meant +// to be as simple and versatile to make the process of generating test samples easier +// TODO: Consider making it easier to create custom cli flags on a per GVK basis for the `create api` and `create webhook` subcommands +type GenericSample struct { + domain string + repo string + gvks []schema.GroupVersionKind + commandContext command.CommandContext + name string + binary string + plugins []string + + initOptions []string + apiOptions []string + webhookOptions []string +} + +// GenericSampleOption is a function that modifies the values in a GenericSample to be used when creating a new GenericSample +type GenericSampleOption func(gs *GenericSample) + +// WithDomain sets the domain to be used during scaffold execution +func WithDomain(domain string) GenericSampleOption { + return func(gs *GenericSample) { + gs.domain = domain + } +} + +// WithRepository sets the repository to be used during scaffold execution +func WithRepository(repo string) GenericSampleOption { + return func(gs *GenericSample) { + gs.repo = repo + } +} + +// WithGvk sets the GroupVersionKind to be used during scaffold execution +func WithGvk(gvks ...schema.GroupVersionKind) GenericSampleOption { + return func(gs *GenericSample) { + gs.gvks = make([]schema.GroupVersionKind, len(gvks)) + copy(gs.gvks, gvks) + } +} + +// WithName sets the name of the sample that is scaffolded +func WithName(name string) GenericSampleOption { + return func(gs *GenericSample) { + gs.name = name + } +} + +// WithCommandContext sets the CommandContext that is used to execute scaffold commands +func WithCommandContext(commandContext command.CommandContext) GenericSampleOption { + return func(gs *GenericSample) { + gs.commandContext = commandContext + } +} + +// WithBinary sets the binary that should be used to run scaffold commands +func WithBinary(binary string) GenericSampleOption { + return func(gs *GenericSample) { + gs.binary = binary + } +} + +// WithPlugins sets the plugins that should be used during scaffolding +func WithPlugins(plugins ...string) GenericSampleOption { + return func(gs *GenericSample) { + gs.plugins = make([]string, len(plugins)) + copy(gs.plugins, plugins) + } +} + +// WithExtraInitOptions sets any additional options that should be passed into an init subcommand +func WithExtraInitOptions(options ...string) GenericSampleOption { + return func(gs *GenericSample) { + gs.initOptions = make([]string, len(options)) + copy(gs.initOptions, options) + } +} + +// WithExtraApiOptions sets any additional options that should be passed into a create api subcommand +func WithExtraApiOptions(options ...string) GenericSampleOption { + return func(gs *GenericSample) { + gs.apiOptions = make([]string, len(options)) + copy(gs.apiOptions, options) + } +} + +// WithExtraWebhookOptions sets any additional options that should be passed into a create webhook subcommand +func WithExtraWebhookOptions(options ...string) GenericSampleOption { + return func(gs *GenericSample) { + gs.webhookOptions = make([]string, len(options)) + copy(gs.webhookOptions, options) + } +} + +// NewGenericSample will return a new GenericSample object. The values used in the GenericSample can be modified using GenericSampleOption functions +func NewGenericSample(opts ...GenericSampleOption) *GenericSample { + gs := &GenericSample{ + domain: "example.com", + name: "generic-sample", + gvks: []schema.GroupVersionKind{ + { + Group: "sample", + Version: "v1", + Kind: "Generic", + }, + }, + // by default use kubebuilder unless otherwise specified + binary: "kubebuilder", + repo: "", + commandContext: command.NewGenericCommandContext(), + plugins: []string{"go/v3"}, + } + + for _, opt := range opts { + opt(gs) + } + + return gs +} + +// CommandContext returns the CommandContext that the GenericSample is using +func (gs *GenericSample) CommandContext() command.CommandContext { + return gs.commandContext +} + +// Name returns the name of the GenericSample +func (gs *GenericSample) Name() string { + return gs.name +} + +// GVKs returns the list of GVKs that is used for creating apis and webhooks +func (gs *GenericSample) GVKs() []schema.GroupVersionKind { + return gs.gvks +} + +// Dir returns the directory the sample is created in +func (gs *GenericSample) Dir() string { + return gs.commandContext.Dir() + "/" + gs.name +} + +// Binary returns the binary used when creating the sample +func (gs *GenericSample) Binary() string { + return gs.binary +} + +// Domain returns the domain of the GenericSample +func (gs *GenericSample) Domain() string { + return gs.domain +} + +// GenerateInit runs the `init` subcommand of the binary provided +func (gs *GenericSample) GenerateInit() error { + options := []string{ + "init", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--domain", + gs.domain, + } + + if gs.repo != "" { + options = append(options, "--repo", gs.repo) + } + + options = append(options, gs.initOptions...) + + ex := exec.Command(gs.binary, options...) + + output, err := gs.commandContext.Run(ex, gs.name) + if err != nil { + return fmt.Errorf("error running command: %w | output: %s", err, string(output)) + } + + return nil +} + +// GenerateApi runs the `create api` subcommand of the binary provided +func (gs *GenericSample) GenerateApi() error { + for _, gvk := range gs.gvks { + options := []string{ + "create", + "api", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--group", + gvk.Group, + "--version", + gvk.Version, + "--kind", + gvk.Kind, + } + + options = append(options, gs.apiOptions...) + + ex := exec.Command(gs.binary, options...) + + output, err := gs.commandContext.Run(ex, gs.name) + if err != nil { + return fmt.Errorf("error running command: %w | output: %s", err, string(output)) + } + } + + return nil +} + +// GenerateWebhook runs the `create webhook` subcommand of the binary provided +func (gs *GenericSample) GenerateWebhook() error { + for _, gvk := range gs.gvks { + options := []string{ + "create", + "webhook", + "--plugins", + strings.TrimRight(strings.Join(gs.plugins, ","), ","), + "--group", + gvk.Group, + "--version", + gvk.Version, + "--kind", + gvk.Kind, + } + + options = append(options, gs.webhookOptions...) + + ex := exec.Command(gs.binary, options...) + + output, err := gs.commandContext.Run(ex, gs.name) + if err != nil { + return fmt.Errorf("error running command: %w | output: %s", err, string(output)) + } + } + + return nil +} diff --git a/test/common/scorecard.go b/test/common/scorecard.go deleted file mode 100644 index 5f4ffcb..0000000 --- a/test/common/scorecard.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2021 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package common - -import ( - "encoding/json" - "fmt" - "os/exec" - "strings" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/operator-framework/ansible-operator-plugins/internal/testutils" - "github.com/operator-framework/api/pkg/apis/scorecard/v1alpha3" -) - -// ScorecardSpec runs a set of scorecard tests common to all operator types. -func ScorecardSpec(tc *testutils.TestContext, operatorType string) func() { - return func() { - var ( - err error - cmd *exec.Cmd - outputBytes []byte - output v1alpha3.TestList - ) - - It("should run a single scorecard test successfully", func() { - cmd = exec.Command(tc.BinaryName, "scorecard", "bundle", - "--selector", "suite=basic", - "--output", "json", - "--wait-time", "2m") - outputBytes, err = tc.Run(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(json.Unmarshal(outputBytes, &output)).To(Succeed()) - - Expect(output.Items).To(HaveLen(1)) - results := output.Items[0].Status.Results - Expect(results).To(HaveLen(1)) - Expect(results[0].Name).To(Equal("basic-check-spec")) - Expect(results[0].State).To(Equal(v1alpha3.PassState)) - }) - - It("should run all enabled scorecard tests successfully", func() { - cmd = exec.Command(tc.BinaryName, "scorecard", "bundle", - "--output", "json", - "--wait-time", "4m") - outputBytes, err = tc.Run(cmd) - // Some tests are expected to fail, which results in scorecard exiting 1. - // Go tests no longer expect to fail - if strings.ToLower(operatorType) != "go" { - Expect(err).To(HaveOccurred()) - } - Expect(json.Unmarshal(outputBytes, &output)).To(Succeed()) - - expected := map[string]v1alpha3.State{ - // Basic suite. - "basic-check-spec": v1alpha3.PassState, - // OLM suite. - "olm-bundle-validation": v1alpha3.PassState, - "olm-crds-have-validation": v1alpha3.FailState, - "olm-crds-have-resources": v1alpha3.FailState, - "olm-spec-descriptors": v1alpha3.FailState, - // For Ansible/Helm should PASS with a Suggestion - // For Golang should pass because we have status spec and descriptions - "olm-status-descriptors": v1alpha3.PassState, - } - if strings.ToLower(operatorType) == "go" { - // Go projects have generated CRD validation. - expected["olm-crds-have-validation"] = v1alpha3.PassState - // Go generated test operator now has CSV markers - // that allows these validations to pass - expected["olm-crds-have-resources"] = v1alpha3.PassState - expected["olm-spec-descriptors"] = v1alpha3.PassState - expected["olm-status-descriptors"] = v1alpha3.PassState - // The Go sample project tests a custom suite. - expected["customtest1"] = v1alpha3.PassState - expected["customtest2"] = v1alpha3.PassState - } - - Expect(output.Items).To(HaveLen(len(expected))) - for i := 0; i < len(output.Items); i++ { - results := output.Items[i].Status.Results - Expect(results).To(HaveLen(1)) - Expect(results[0].Name).NotTo(BeEmpty()) - fmt.Fprintln(GinkgoWriter, " - Name: ", results[0].Name) - fmt.Fprintln(GinkgoWriter, " Expected: ", expected[results[0].Name]) - fmt.Fprintln(GinkgoWriter, " Output: ", results[0].State) - Expect(results[0].State).To(Equal(expected[results[0].Name])) - } - }) - - It("should configure scorecard storage successfully", func() { - cmd = exec.Command(tc.BinaryName, "scorecard", "bundle", - "--selector", "suite=basic", - "--output", "json", - "--test-output", "/testdata", - "--wait-time", "4m") - outputBytes, err = tc.Run(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(json.Unmarshal(outputBytes, &output)).To(Succeed()) - - Expect(output.Items).To(HaveLen(1)) - results := output.Items[0].Status.Results - Expect(results).To(HaveLen(1)) - Expect(results[0].Name).To(Equal("basic-check-spec")) - Expect(results[0].State).To(Equal(v1alpha3.PassState)) - }) - } -} diff --git a/test/e2e/ansible/cluster_test.go b/test/e2e/ansible/cluster_test.go index 0eed685..73167d7 100644 --- a/test/e2e/ansible/cluster_test.go +++ b/test/e2e/ansible/cluster_test.go @@ -15,7 +15,6 @@ package e2e_ansible_test import ( - "encoding/base64" "fmt" "path/filepath" "strings" @@ -23,10 +22,12 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime/schema" kbtutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" "github.com/operator-framework/ansible-operator-plugins/internal/testutils" - "github.com/operator-framework/ansible-operator-plugins/test/common" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/metrics" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/operator" ) var _ = Describe("Running ansible projects", func() { @@ -34,104 +35,78 @@ var _ = Describe("Running ansible projects", func() { var ( controllerPodName, memcachedDeploymentName, metricsClusterRoleBindingName string fooSampleFile, memfinSampleFile, memcachedSampleFile string + gvk schema.GroupVersionKind ) Context("built with operator-sdk", func() { BeforeEach(func() { - metricsClusterRoleBindingName = fmt.Sprintf("%s-metrics-reader", tc.ProjectName) - samplesDir := filepath.Join(tc.Dir, "config", "samples") - fooSampleFile = filepath.Join(samplesDir, fmt.Sprintf("%s_%s_foo.yaml", tc.Group, tc.Version)) - memfinSampleFile = filepath.Join(samplesDir, fmt.Sprintf("%s_%s_memfin.yaml", tc.Group, tc.Version)) + gvk = ansibleSample.GVKs()[0] + metricsClusterRoleBindingName = fmt.Sprintf("%s-metrics-reader", ansibleSample.Name()) + samplesDir := filepath.Join("config", "samples") + fooSampleFile = filepath.Join(samplesDir, fmt.Sprintf("%s_%s_foo.yaml", gvk.Group, gvk.Version)) + memfinSampleFile = filepath.Join(samplesDir, fmt.Sprintf("%s_%s_memfin.yaml", gvk.Group, gvk.Version)) memcachedSampleFile = filepath.Join(samplesDir, - fmt.Sprintf("%s_%s_%s.yaml", tc.Group, tc.Version, strings.ToLower(tc.Kind))) + fmt.Sprintf("%s_%s_%s.yaml", gvk.Group, gvk.Version, strings.ToLower(gvk.Kind))) + + By("installing the CRDs on the cluster") + Expect(operator.InstallCRDs(ansibleSample)).To(Succeed()) By("deploying project on the cluster") - Expect(tc.Make("deploy", "IMG="+tc.ImageName)).To(Succeed()) + Expect(operator.DeployOperator(ansibleSample, image)).To(Succeed()) }) AfterEach(func() { - By("deleting curl pod") - testutils.WrapWarnOutput(tc.Kubectl.Delete(false, "pod", "curl")) - - By("deleting test CR instances") - for _, sample := range []string{memcachedSampleFile, fooSampleFile, memfinSampleFile} { - testutils.WrapWarnOutput(tc.Kubectl.Delete(false, "-f", sample)) - } + By("cleaning up metrics") + Expect(metrics.CleanUpMetrics(kctl, metricsClusterRoleBindingName)).To(Succeed()) - By("cleaning up permissions") - testutils.WrapWarnOutput(tc.Kubectl.Command("delete", "clusterrolebinding", metricsClusterRoleBindingName)) - - By("undeploy project") - testutils.WrapWarn(tc.Make("undeploy")) + By("cleaning up created API objects during test process") + Expect(operator.UndeployOperator(ansibleSample)).To(Succeed()) By("ensuring that the namespace was deleted") - testutils.WrapWarnOutput(tc.Kubectl.Wait(false, "namespace", "foo", "--for", "delete", "--timeout", "2m")) + testutils.WrapWarnOutput(kctl.Wait(false, "namespace", "foo", "--for", "delete", "--timeout", "2m")) }) It("should run correctly in a cluster", func() { By("checking if the Operator project Pod is running") verifyControllerUp := func() error { - // Get the controller-manager pod name - podOutput, err := tc.Kubectl.Get( - true, - "pods", "-l", "control-plane=controller-manager", - "-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+ - "{{ \"\\n\" }}{{ end }}{{ end }}") - if err != nil { - return fmt.Errorf("could not get pods: %v", err) - } - podNames := kbtutil.GetNonEmptyLines(podOutput) - if len(podNames) != 1 { - return fmt.Errorf("expecting 1 pod, have %d", len(podNames)) - } - controllerPodName = podNames[0] - if !strings.Contains(controllerPodName, "controller-manager") { - return fmt.Errorf("expecting pod name %q to contain %q", controllerPodName, "controller-manager") - } - - // Ensure the controller-manager Pod is running. - status, err := tc.Kubectl.Get( - true, - "pods", controllerPodName, "-o", "jsonpath={.status.phase}") - if err != nil { - return fmt.Errorf("failed to get pod status for %q: %v", controllerPodName, err) - } - if status != "Running" { - return fmt.Errorf("controller pod in %s status", status) - } - return nil + var err error + controllerPodName, err = operator.EnsureOperatorRunning(kctl, 1, "controller-manager", "controller-manager") + return err } Eventually(verifyControllerUp, 2*time.Minute, time.Second).Should(Succeed()) By("ensuring the created ServiceMonitor for the manager") - _, err := tc.Kubectl.Get( + _, err := kctl.Get( true, "ServiceMonitor", - fmt.Sprintf("%s-controller-manager-metrics-monitor", tc.ProjectName)) + fmt.Sprintf("%s-controller-manager-metrics-monitor", ansibleSample.Name())) Expect(err).NotTo(HaveOccurred()) By("ensuring the created metrics Service for the manager") - _, err = tc.Kubectl.Get( + _, err = kctl.Get( true, "Service", - fmt.Sprintf("%s-controller-manager-metrics-service", tc.ProjectName)) + fmt.Sprintf("%s-controller-manager-metrics-service", ansibleSample.Name())) Expect(err).NotTo(HaveOccurred()) + // TODO(everettraven): this could be simplifed once all the GVKs are made to be part of the sample implementation + // ----------------------------------------------- By("create custom resource (Memcached CR)") - _, err = tc.Kubectl.Apply(false, "-f", memcachedSampleFile) + _, err = kctl.Apply(false, "-f", memcachedSampleFile) Expect(err).NotTo(HaveOccurred()) By("create custom resource (Foo CR)") - _, err = tc.Kubectl.Apply(false, "-f", fooSampleFile) + _, err = kctl.Apply(false, "-f", fooSampleFile) Expect(err).NotTo(HaveOccurred()) By("create custom resource (Memfin CR)") - _, err = tc.Kubectl.Apply(false, "-f", memfinSampleFile) + _, err = kctl.Apply(false, "-f", memfinSampleFile) Expect(err).NotTo(HaveOccurred()) + // ----------------------------------------------- By("ensuring the CR gets reconciled") managerContainerLogs := func() string { - logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager") + logOutput, err := kctl.Logs(true, controllerPodName, "-c", "manager") Expect(err).NotTo(HaveOccurred()) return logOutput } @@ -143,7 +118,7 @@ var _ = Describe("Running ansible projects", func() { By("ensuring no liveness probe fail events") verifyControllerProbe := func() string { By("getting the controller-manager events") - eventsOutput, err := tc.Kubectl.Get( + eventsOutput, err := kctl.Get( true, "events", "--field-selector", fmt.Sprintf("involvedObject.name=%s", controllerPodName)) Expect(err).NotTo(HaveOccurred()) @@ -153,7 +128,7 @@ var _ = Describe("Running ansible projects", func() { By("getting memcached deploy by labels") getMemcachedDeploymentName := func() string { - memcachedDeploymentName, err = tc.Kubectl.Get( + memcachedDeploymentName, err = kctl.Get( false, "deployment", "-l", "app=memcached", "-o", "jsonpath={..metadata.name}") Expect(err).NotTo(HaveOccurred()) @@ -163,7 +138,7 @@ var _ = Describe("Running ansible projects", func() { By("checking the Memcached CR deployment status") verifyCRUp := func() string { - output, err := tc.Kubectl.Command( + output, err := kctl.Command( "rollout", "status", "deployment", memcachedDeploymentName) Expect(err).NotTo(HaveOccurred()) return output @@ -171,7 +146,7 @@ var _ = Describe("Running ansible projects", func() { Eventually(verifyCRUp, time.Minute, time.Second).Should(ContainSubstring("successfully rolled out")) By("ensuring the created Service for the Memcached CR") - crServiceName, err := tc.Kubectl.Get( + crServiceName, err := kctl.Get( false, "Service", "-l", "app=memcached") Expect(err).NotTo(HaveOccurred()) @@ -179,7 +154,7 @@ var _ = Describe("Running ansible projects", func() { By("Verifying that a config map owned by the CR has been created") verifyConfigMap := func() error { - _, err = tc.Kubectl.Get( + _, err = kctl.Get( false, "configmap", "test-blacklist-watches") return err @@ -188,7 +163,7 @@ var _ = Describe("Running ansible projects", func() { By("Ensuring that config map requests skip the cache.") checkSkipCache := func() string { - logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager") + logOutput, err := kctl.Logs(true, controllerPodName, "-c", "manager") Expect(err).NotTo(HaveOccurred()) return logOutput } @@ -197,13 +172,13 @@ var _ = Describe("Running ansible projects", func() { "\"Path\":\"/api/v1/namespaces/default/configmaps/test-blacklist-watches\"")) By("scaling deployment replicas to 2") - _, err = tc.Kubectl.Command( + _, err = kctl.Command( "scale", "deployment", memcachedDeploymentName, "--replicas", "2") Expect(err).NotTo(HaveOccurred()) By("verifying the deployment automatically scales back down to 1") verifyMemcachedScalesBack := func() error { - replicas, err := tc.Kubectl.Get( + replicas, err := kctl.Get( false, "deployment", memcachedDeploymentName, "-o", "jsonpath={..spec.replicas}") Expect(err).NotTo(HaveOccurred()) @@ -215,16 +190,16 @@ var _ = Describe("Running ansible projects", func() { Eventually(verifyMemcachedScalesBack, time.Minute, time.Second).Should(Succeed()) By("updating size to 2 in the CR manifest") - err = kbtutil.ReplaceInFile(memcachedSampleFile, "size: 1", "size: 2") + err = kbtutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), memcachedSampleFile), "size: 1", "size: 2") Expect(err).NotTo(HaveOccurred()) By("applying CR manifest with size: 2") - _, err = tc.Kubectl.Apply(false, "-f", memcachedSampleFile) + _, err = kctl.Apply(false, "-f", memcachedSampleFile) Expect(err).NotTo(HaveOccurred()) By("ensuring the CR gets reconciled after patching it") managerContainerLogsAfterUpdateCR := func() string { - logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager") + logOutput, err := kctl.Logs(true, controllerPodName, "-c", "manager") Expect(err).NotTo(HaveOccurred()) return logOutput } @@ -234,7 +209,7 @@ var _ = Describe("Running ansible projects", func() { By("checking Deployment replicas spec is equals 2") verifyMemcachedPatch := func() error { - replicas, err := tc.Kubectl.Get( + replicas, err := kctl.Get( false, "deployment", memcachedDeploymentName, "-o", "jsonpath={..spec.replicas}") Expect(err).NotTo(HaveOccurred()) @@ -245,84 +220,16 @@ var _ = Describe("Running ansible projects", func() { } Eventually(verifyMemcachedPatch, time.Minute, time.Second).Should(Succeed()) - // As of Kubernetes 1.24 a ServiceAccount no longer has a ServiceAccount token secret autogenerated. We have to create it manually here - By("Creating the ServiceAccount token") - secretFile, err := common.GetSASecret(tc.Kubectl.ServiceAccount, tc.Dir) - Expect(err).NotTo(HaveOccurred()) - Eventually(func() error { - _, err = tc.Kubectl.Apply(true, "-f", secretFile) - return err - }, time.Minute, time.Second).Should(Succeed()) - By("annotating the CR") - _, err = tc.Kubectl.Command( - "annotate", "foo", "foo-sample", "test-annotation='step2'") - Expect(err).NotTo(HaveOccurred()) - - Eventually(managerContainerLogs, time.Minute, time.Second).Should(ContainSubstring( - "Ansible-runner exited successfully")) - Eventually(managerContainerLogs, time.Minute, time.Second).Should(ContainSubstring( - "test-annotation found : 'step2'")) - Eventually(managerContainerLogs, time.Minute, time.Second).ShouldNot(ContainSubstring("failed=1")) - Eventually(managerContainerLogs, time.Minute, time.Second).ShouldNot(ContainSubstring("[Gathering Facts]")) - - By("granting permissions to access the metrics and read the token") - _, err = tc.Kubectl.Command("create", "clusterrolebinding", metricsClusterRoleBindingName, - fmt.Sprintf("--clusterrole=%s-metrics-reader", tc.ProjectName), - fmt.Sprintf("--serviceaccount=%s:%s", tc.Kubectl.Namespace, tc.Kubectl.ServiceAccount)) - Expect(err).NotTo(HaveOccurred()) - - By("reading the metrics token") - // Filter token query by service account in case more than one exists in a namespace. - query := fmt.Sprintf(`{.items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="%s")].data.token}`, - tc.Kubectl.ServiceAccount, - ) - b64Token, err := tc.Kubectl.Get(true, "secrets", "-o=jsonpath="+query) - Expect(err).NotTo(HaveOccurred()) - token, err := base64.StdEncoding.DecodeString(strings.TrimSpace(b64Token)) - Expect(err).NotTo(HaveOccurred()) - Expect(token).ToNot(BeEmpty()) - - By("creating a curl pod") - cmdOpts := []string{ - "run", "curl", "--image=curlimages/curl:7.68.0", "--restart=OnFailure", "--", - "curl", "-v", "-k", "-H", fmt.Sprintf(`Authorization: Bearer %s`, token), - fmt.Sprintf("https://%s-controller-manager-metrics-service.%s.svc:8443/metrics", tc.ProjectName, tc.Kubectl.Namespace), - } - _, err = tc.Kubectl.CommandInNamespace(cmdOpts...) - Expect(err).NotTo(HaveOccurred()) - - By("validating that the curl pod is running as expected") - verifyCurlUp := func() error { - // Validate pod status - status, err := tc.Kubectl.Get( - true, - "pods", "curl", "-o", "jsonpath={.status.phase}") - if err != nil { - return err - } - if status != "Completed" && status != "Succeeded" { - return fmt.Errorf("curl pod in %s status", status) - } - return nil - } - Eventually(verifyCurlUp, 2*time.Minute, time.Second).Should(Succeed()) - - By("checking metrics endpoint serving as expected") - getCurlLogs := func() string { - logOutput, err := tc.Kubectl.Logs("curl") - Expect(err).NotTo(HaveOccurred()) - return logOutput - } - Eventually(getCurlLogs, time.Minute, time.Second).Should(ContainSubstring("< HTTP/2 200")) + metricInfo := metrics.GetMetrics(ansibleSample, kctl, metricsClusterRoleBindingName) By("getting the CR namespace token") - crNamespace, err := tc.Kubectl.Get( + crNamespace, err := kctl.Get( false, - tc.Kind, - fmt.Sprintf("%s-sample", strings.ToLower(tc.Kind)), + gvk.Kind, + fmt.Sprintf("%s-sample", strings.ToLower(gvk.Kind)), "-o=jsonpath={..metadata.namespace}") Expect(err).NotTo(HaveOccurred()) - Expect(crNamespace).NotTo(BeEmpty()) + Expect(crNamespace).NotTo(HaveLen(0)) By("ensuring the operator metrics contains a `resource_created_at` metric for the Memcached CR") metricExportedMemcachedCR := fmt.Sprintf("resource_created_at_seconds{group=\"%s\","+ @@ -330,12 +237,12 @@ var _ = Describe("Running ansible projects", func() { "name=\"%s-sample\","+ "namespace=\"%s\","+ "version=\"%s\"}", - fmt.Sprintf("%s.%s", tc.Group, tc.Domain), - tc.Kind, - strings.ToLower(tc.Kind), + fmt.Sprintf("%s.%s", gvk.Group, ansibleSample.Domain()), + gvk.Kind, + strings.ToLower(gvk.Kind), crNamespace, - tc.Version) - Eventually(getCurlLogs, time.Minute, time.Second).Should(ContainSubstring(metricExportedMemcachedCR)) + gvk.Version) + Expect(metricInfo).Should(ContainSubstring(metricExportedMemcachedCR)) By("ensuring the operator metrics contains a `resource_created_at` metric for the Foo CR") metricExportedFooCR := fmt.Sprintf("resource_created_at_seconds{group=\"%s\","+ @@ -343,12 +250,12 @@ var _ = Describe("Running ansible projects", func() { "name=\"%s-sample\","+ "namespace=\"%s\","+ "version=\"%s\"}", - fmt.Sprintf("%s.%s", tc.Group, tc.Domain), + fmt.Sprintf("%s.%s", gvk.Group, ansibleSample.Domain()), "Foo", strings.ToLower("Foo"), crNamespace, - tc.Version) - Eventually(getCurlLogs, time.Minute, time.Second).Should(ContainSubstring(metricExportedFooCR)) + gvk.Version) + Expect(metricInfo).Should(ContainSubstring(metricExportedFooCR)) By("ensuring the operator metrics contains a `resource_created_at` metric for the Memfin CR") metricExportedMemfinCR := fmt.Sprintf("resource_created_at_seconds{group=\"%s\","+ @@ -356,24 +263,24 @@ var _ = Describe("Running ansible projects", func() { "name=\"%s-sample\","+ "namespace=\"%s\","+ "version=\"%s\"}", - fmt.Sprintf("%s.%s", tc.Group, tc.Domain), + fmt.Sprintf("%s.%s", gvk.Group, ansibleSample.Domain()), "Memfin", strings.ToLower("Memfin"), crNamespace, - tc.Version) - Eventually(getCurlLogs, time.Minute, time.Second).Should(ContainSubstring(metricExportedMemfinCR)) + gvk.Version) + Expect(metricInfo).Should(ContainSubstring(metricExportedMemfinCR)) By("creating a configmap that the finalizer should remove") - _, err = tc.Kubectl.Command("create", "configmap", "deleteme") + _, err = kctl.Command("create", "configmap", "deleteme") Expect(err).NotTo(HaveOccurred()) By("deleting Memcached CR manifest") - _, err = tc.Kubectl.Delete(false, "-f", memcachedSampleFile) + _, err = kctl.Delete(false, "-f", memcachedSampleFile) Expect(err).NotTo(HaveOccurred()) By("ensuring the CR gets reconciled successfully") managerContainerLogsAfterDeleteCR := func() string { - logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager") + logOutput, err := kctl.Logs(true, controllerPodName, "-c", "manager") Expect(err).NotTo(HaveOccurred()) return logOutput } @@ -383,7 +290,7 @@ var _ = Describe("Running ansible projects", func() { By("ensuring that Memchaced Deployment was removed") getMemcachedDeployment := func() error { - _, err := tc.Kubectl.Get( + _, err := kctl.Get( false, "deployment", memcachedDeploymentName) return err diff --git a/test/e2e/ansible/local_test.go b/test/e2e/ansible/local_test.go index 5a04e9b..0731280 100644 --- a/test/e2e/ansible/local_test.go +++ b/test/e2e/ansible/local_test.go @@ -19,30 +19,31 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/operator" ) -var _ = Describe("Running ansible projects", func() { +var _ = Describe("Running Ansible projects", func() { Context("built with operator-sdk", func() { - BeforeEach(func() { - By("installing CRD's") - err := tc.Make("install") + By("Installing CRD's") + err := operator.InstallCRDs(ansibleSample) Expect(err).NotTo(HaveOccurred()) }) AfterEach(func() { - By("uninstalling CRD's") - err := tc.Make("uninstall") + By("Uninstalling CRD's") + err := operator.UninstallCRDs(ansibleSample) Expect(err).NotTo(HaveOccurred()) }) - It("should run correctly locally", func() { - By("running the project") + It("Should run correctly when run locally", func() { + By("Running the project") cmd := exec.Command("make", "run") + cmd.Dir = ansibleSample.Dir() err := cmd.Start() Expect(err).NotTo(HaveOccurred()) - By("killing the project") + By("Killing the project") err = cmd.Process.Kill() Expect(err).NotTo(HaveOccurred()) }) diff --git a/test/e2e/ansible/olm_test.go b/test/e2e/ansible/olm_test.go deleted file mode 100644 index d1fcec8..0000000 --- a/test/e2e/ansible/olm_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package e2e_ansible_test - -import ( - "os/exec" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/operator-framework/ansible-operator-plugins/internal/util/projutil" -) - -var _ = Describe("Integrating ansible Projects with OLM", func() { - Context("with operator-sdk", func() { - const operatorVersion = "0.0.1" - - It("should generate and run a valid OLM bundle and packagemanifests", func() { - By("building the operator bundle image") - err := tc.Make("bundle-build", "BUNDLE_IMG="+tc.BundleImageName) - Expect(err).NotTo(HaveOccurred()) - - By("adding the 'packagemanifests' rule to the Makefile") - err = tc.AddPackagemanifestsTarget(projutil.OperatorTypeAnsible) - Expect(err).NotTo(HaveOccurred()) - - By("generating the operator package manifests") - err = tc.Make("packagemanifests", "IMG="+tc.ImageName) - Expect(err).NotTo(HaveOccurred()) - - By("running the package") - runPkgManCmd := exec.Command(tc.BinaryName, "run", "packagemanifests", - "--install-mode", "AllNamespaces", - "--version", operatorVersion, - "--timeout", "4m") - _, err = tc.Run(runPkgManCmd) - Expect(err).NotTo(HaveOccurred()) - - By("destroying the deployed package manifests-formatted operator") - cleanupPkgManCmd := exec.Command(tc.BinaryName, "cleanup", tc.ProjectName, - "--timeout", "4m") - _, err = tc.Run(cleanupPkgManCmd) - Expect(err).NotTo(HaveOccurred()) - }) - }) -}) diff --git a/test/e2e/ansible/scorecard_test.go b/test/e2e/ansible/scorecard_test.go deleted file mode 100644 index f17ec21..0000000 --- a/test/e2e/ansible/scorecard_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package e2e_ansible_test - -// import ( -// . "github.com/onsi/ginkgo/v2" -// ) - -// For now don't run Scorecard tests -// var _ = Describe("scorecard", common.ScorecardSpec(&tc, "ansible")) diff --git a/test/e2e/ansible/suite_test.go b/test/e2e/ansible/suite_test.go index 9db22f7..0c671a7 100644 --- a/test/e2e/ansible/suite_test.go +++ b/test/e2e/ansible/suite_test.go @@ -16,19 +16,30 @@ package e2e_ansible_test import ( "fmt" + "os" "os/exec" "path" "path/filepath" "strings" "testing" + "time" + + kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" - "github.com/operator-framework/ansible-operator-plugins/internal/testutils" + "github.com/operator-framework/ansible-operator-plugins/hack/generate/samples/ansible" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/kind" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/operator" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/e2e/prometheus" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/kubernetes" + "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" ) +//TODO: update this to use the new PoC api + // TestE2EAnsible ensures the ansible projects built with the SDK tool by using its binary. func TestE2EAnsible(t *testing.T) { if testing.Short() { @@ -39,122 +50,155 @@ func TestE2EAnsible(t *testing.T) { } var ( - tc testutils.TestContext + kctl kubernetes.Kubectl + isPrometheusManagedBySuite = true + ansibleSample sample.Sample + testdir = "e2e-test-ansible" + image = "e2e-test-ansible:temp" ) // BeforeSuite run before any specs are run to perform the required actions for all e2e ansible tests. var _ = BeforeSuite(func() { - var err error - - By("creating a new test context") - tc, err = testutils.NewTestContext(path.Join("../../../../", testutils.BinaryName), "GO111MODULE=on") + wd, err := os.Getwd() Expect(err).NotTo(HaveOccurred()) - fmt.Println(tc.Dir) + samples := ansible.GenerateMoleculeSample(path.Join(wd, testdir)) + ansibleSample = samples[0] - tc.Domain = "example.com" - tc.Version = "v1alpha1" - tc.Group = "cache" - tc.Kind = "Memcached" - tc.ProjectName = "memcached-operator" - tc.Kubectl.Namespace = fmt.Sprintf("%s-system", tc.ProjectName) - tc.Kubectl.ServiceAccount = fmt.Sprintf("%s-controller-manager", tc.ProjectName) + kctl = kubernetes.NewKubectlUtil( + kubernetes.WithCommandContext( + command.NewGenericCommandContext( + command.WithDir(ansibleSample.Dir()), + ), + ), + kubernetes.WithNamespace(ansibleSample.Name()+"-system"), + kubernetes.WithServiceAccount(ansibleSample.Name()+"-controller-manager"), + ) - By("copying sample to a temporary e2e directory") - Expect(exec.Command("cp", "-r", "../../../testdata/ansible/memcached-operator", tc.Dir).Run()).To(Succeed()) + // TODO(everettraven): IMO this should be moved to the logic for implementing the sample. Keeping as is for now to make finish the PoC easier + // --------------------------------------------------- By("enabling debug logging in the manager") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "config", "default", "manager_auth_proxy_patch.yaml"), + err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "config", "default", "manager_auth_proxy_patch.yaml"), "- \"--leader-elect\"", "- \"--zap-log-level=2\"\n - \"--leader-elect\"") Expect(err).NotTo(HaveOccurred()) - By("preparing the prerequisites on cluster") - tc.InstallPrerequisites() - - By("using dev image for scorecard-test") - err = tc.ReplaceScorecardImagesForDev() - Expect(err).NotTo(HaveOccurred()) - - By("replacing project Dockerfile to use ansible base image with the dev tag") - err = kbutil.ReplaceRegexInFile(filepath.Join(tc.Dir, "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") - Expect(err).Should(Succeed()) - - By("adding Memcached mock task to the role") - err = kbutil.InsertCode(filepath.Join(tc.Dir, "roles", strings.ToLower(tc.Kind), "tasks", "main.yml"), - "periodSeconds: 3", memcachedWithBlackListTask) - Expect(err).NotTo(HaveOccurred()) - - By("creating an API definition to add a task to delete the config map") - err = tc.CreateAPI( - "--group", tc.Group, - "--version", tc.Version, - "--kind", "Memfin", - "--generate-role") - Expect(err).NotTo(HaveOccurred()) - - By("updating spec of Memfin sample") - err = kbutil.ReplaceInFile( - filepath.Join(tc.Dir, "config", "samples", fmt.Sprintf("%s_%s_memfin.yaml", tc.Group, tc.Version)), - "# TODO(user): Add fields here", - "foo: bar") - Expect(err).NotTo(HaveOccurred()) - - By("adding task to delete config map") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "roles", "memfin", "tasks", "main.yml"), - "# tasks file for Memfin", taskToDeleteConfigMap) - Expect(err).NotTo(HaveOccurred()) - - By("adding to watches finalizer and blacklist") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "watches.yaml"), - "playbook: playbooks/memcached.yml", memcachedWatchCustomizations) - Expect(err).NotTo(HaveOccurred()) - - By("create API to test watching multiple GVKs") - err = tc.CreateAPI( - "--group", tc.Group, - "--version", tc.Version, - "--kind", "Foo", - "--generate-role") - Expect(err).NotTo(HaveOccurred()) + // --------------------------------------------------- - By("updating spec of Foo sample") - err = kbutil.ReplaceInFile( - filepath.Join(tc.Dir, "config", "samples", fmt.Sprintf("%s_%s_foo.yaml", tc.Group, tc.Version)), - "# TODO(user): Add fields here", - "foo: bar") + By("checking API resources applied on Cluster") + output, err := kctl.Command("api-resources") + fmt.Println("output:", output) Expect(err).NotTo(HaveOccurred()) + if strings.Contains(output, "servicemonitors") { + isPrometheusManagedBySuite = false + } - By("adding task to display annotations of Foo") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "roles", "foo", "tasks", "main.yml"), - "# tasks file for Foo", fooDebugAnnotations) - Expect(err).NotTo(HaveOccurred()) + if isPrometheusManagedBySuite { + By("installing Prometheus") + Expect(prometheus.InstallPrometheusOperator(kctl)).To(Succeed()) + + By("ensuring provisioned Prometheus Manager Service") + Eventually(func() error { + _, err := kctl.Get( + false, + "Service", "prometheus-operator") + return err + }, 3*time.Minute, time.Second).Should(Succeed()) + } - By("adding to watches annotations changes") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "watches.yaml"), - "role: foo", fooWatchCustomizations) - Expect(err).NotTo(HaveOccurred()) + // By("replacing project Dockerfile to use ansible base image with the dev tag") + // err = kbutil.ReplaceRegexInFile(filepath.Join(ansibleSample.Dir(), "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") + // Expect(err).Should(Succeed()) + + // TODO(everettraven): IMO this should be moved to the logic for implementing the sample. Keeping as is for now to make finish the PoC easier + // --------------------------------------------------- + + // gvk := ansibleSample.GVKs()[0] + + // By("adding Memcached mock task to the role") + // err = kbutil.InsertCode(filepath.Join(ansibleSample.Dir(), "roles", strings.ToLower(gvk.Kind), "tasks", "main.yml"), + // "periodSeconds: 3", memcachedWithBlackListTask) + // Expect(err).NotTo(HaveOccurred()) + + // By("creating an API definition to add a task to delete the config map") + // cmd := exec.Command(ansibleSample.Binary(), "create", "api", + // "--group", gvk.Group, + // "--version", gvk.Version, + // "--kind", "Memfin", + // "--generate-role") + // _, err = ansibleSample.CommandContext().Run(cmd, ansibleSample.Name()) + // Expect(err).NotTo(HaveOccurred()) + + // By("updating spec of Memfin sample") + // err = kbutil.ReplaceInFile( + // filepath.Join(ansibleSample.Dir(), "config", "samples", fmt.Sprintf("%s_%s_memfin.yaml", gvk.Group, gvk.Version)), + // "# TODO(user): Add fields here", + // "foo: bar") + // Expect(err).NotTo(HaveOccurred()) + + // By("adding task to delete config map") + // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "roles", "memfin", "tasks", "main.yml"), + // "# tasks file for Memfin", taskToDeleteConfigMap) + // Expect(err).NotTo(HaveOccurred()) + + // By("adding to watches finalizer and blacklist") + // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "watches.yaml"), + // "playbook: playbooks/memcached.yml", memcachedWatchCustomizations) + // Expect(err).NotTo(HaveOccurred()) + + // By("create API to test watching multiple GVKs") + // cmd = exec.Command(ansibleSample.Binary(), "create", "api", + // "--group", gvk.Group, + // "--version", gvk.Version, + // "--kind", "Foo", + // "--generate-role") + // _, err = ansibleSample.CommandContext().Run(cmd, ansibleSample.Name()) + // Expect(err).NotTo(HaveOccurred()) + + // By("updating spec of Foo sample") + // err = kbutil.ReplaceInFile( + // filepath.Join(ansibleSample.Dir(), "config", "samples", fmt.Sprintf("%s_%s_foo.yaml", gvk.Group, gvk.Version)), + // "# TODO(user): Add fields here", + // "foo: bar") + // Expect(err).NotTo(HaveOccurred()) + + // By("adding RBAC permissions for the Memcached Kind") + // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "config", "rbac", "role.yaml"), + // "#+kubebuilder:scaffold:rules", rolesForBaseOperator) + // Expect(err).NotTo(HaveOccurred()) + + // --------------------------------------------------- - By("adding RBAC permissions for the Memcached Kind") - err = kbutil.ReplaceInFile(filepath.Join(tc.Dir, "config", "rbac", "role.yaml"), - "#+kubebuilder:scaffold:rules", rolesForBaseOperator) + By("building the project image") + err = operator.BuildOperatorImage(ansibleSample, image) Expect(err).NotTo(HaveOccurred()) - By("building the project image") - err = tc.Make("docker-build", "IMG="+tc.ImageName) + onKind, err := kind.IsRunningOnKind(kctl) Expect(err).NotTo(HaveOccurred()) + if onKind { + By("loading the required images into Kind cluster") + Expect(kind.LoadImageToKindCluster(ansibleSample.CommandContext(), image)).To(Succeed()) + } - By("generating bundle") - Expect(tc.GenerateBundle()).To(Succeed()) }) // AfterSuite run after all the specs have run, regardless of whether any tests have failed to ensures that // all be cleaned up var _ = AfterSuite(func() { By("uninstalling prerequisites") - tc.UninstallPrerequisites() + if isPrometheusManagedBySuite { + By("uninstalling Prometheus") + prometheus.UninstallPrometheusOperator(kctl) + } By("destroying container image and work dir") - tc.Destroy() + cmd := exec.Command("docker", "rmi", "-f", image) + if _, err := ansibleSample.CommandContext().Run(cmd); err != nil { + Expect(err).To(BeNil()) + } + if err := os.RemoveAll(testdir); err != nil { + Expect(err).To(BeNil()) + } }) const memcachedWithBlackListTask = ` @@ -236,23 +280,3 @@ const rolesForBaseOperator = ` - watch #+kubebuilder:scaffold:rules ` -const fooDebugAnnotations = ` -- name: Fetch annotations - k8s_info: - kind: Foo - api_version: cache.example.com/v1alpha1 - name: "{{ ansible_operator_meta.name }}" - namespace: "{{ ansible_operator_meta.namespace }}" - register: foo_cr_info - -- name: Print annotations - debug: - msg: "test-annotation found : {{ foo_cr_info.resources[0].metadata.annotations['test-annotation'] }}" - when: - - foo_cr_info.resources | length > 0 - - "'test-annotation' in foo_cr_info.resources[0].metadata.annotations | default({})" -` - -const fooWatchCustomizations = `role: foo - watchAnnotationsChanges: true -` diff --git a/testdata/ansible/memcached-operator/Makefile b/testdata/ansible/memcached-operator/Makefile deleted file mode 100644 index 21e4773..0000000 --- a/testdata/ansible/memcached-operator/Makefile +++ /dev/null @@ -1,231 +0,0 @@ -# VERSION defines the project version for the bundle. -# Update this value when you upgrade the version of your project. -# To re-generate a bundle for another specific version without changing the standard setup, you can: -# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) -# - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.0.1 - -# CHANNELS define the bundle channels used in the bundle. -# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") -# To re-generate a bundle for other specific channels without changing the standard setup, you can: -# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) -# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") -ifneq ($(origin CHANNELS), undefined) -BUNDLE_CHANNELS := --channels=$(CHANNELS) -endif - -# DEFAULT_CHANNEL defines the default channel used in the bundle. -# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") -# To re-generate a bundle for any other default channel without changing the default setup, you can: -# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) -# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") -ifneq ($(origin DEFAULT_CHANNEL), undefined) -BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) -endif -BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) - -# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. -# This variable is used to construct full image tags for bundle and catalog images. -# -# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# example.com/memcached-operator-bundle:$VERSION and example.com/memcached-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= example.com/memcached-operator - -# BUNDLE_IMG defines the image:tag used for the bundle. -# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) -BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) - -# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command -BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) - -# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests -# You can enable this value if you would like to use SHA Based Digests -# To enable set flag to true -USE_IMAGE_DIGESTS ?= false -ifeq ($(USE_IMAGE_DIGESTS), true) - BUNDLE_GEN_FLAGS += --use-image-digests -endif - -# Set the Operator SDK version to use. By default, what is installed on the system is used. -# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. -OPERATOR_SDK_VERSION ?= v1.31.0 - -# Image URL to use all building/pushing image targets -IMG ?= controller:latest - -.PHONY: all -all: docker-build - -##@ General - -# The help target prints out all targets with their descriptions organized -# beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the -# entire set of makefiles included in this invocation, looking for lines of the -# file as xyz: ## something, and then pretty-format the target and help. Then, -# if there's a line with ##@ something, that gets pretty-printed as a category. -# More info on the usage of ANSI control characters for terminal formatting: -# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters -# More info on the awk command: -# http://linuxcommand.org/lc3_adv_awk.php - -.PHONY: help -help: ## Display this help. - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) - -##@ Build - -.PHONY: run -ANSIBLE_ROLES_PATH?="$(shell pwd)/roles" -run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config - $(ANSIBLE_OPERATOR) run - -.PHONY: docker-build -docker-build: ## Build docker image with the manager. - docker build -t ${IMG} . - -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - docker push ${IMG} - -# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple -# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ -# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> than the export will fail) -# To properly provided solutions that supports more than one platform you should use this option. -PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le -.PHONY: docker-buildx -docker-buildx: test ## Build and push docker image for the manager for cross-platform support - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - docker buildx create --name project-v3-builder - docker buildx use project-v3-builder - - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - docker buildx rm project-v3-builder - rm Dockerfile.cross - -##@ Deployment - -.PHONY: install -install: kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - - -.PHONY: uninstall -uninstall: kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - - -.PHONY: deploy -deploy: kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - - -.PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/default | kubectl delete -f - - -OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') -ARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') - -.PHONY: kustomize -KUSTOMIZE = $(shell pwd)/bin/kustomize -kustomize: ## Download kustomize locally if necessary. -ifeq (,$(wildcard $(KUSTOMIZE))) -ifeq (,$(shell which kustomize 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(KUSTOMIZE)) ;\ - curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.7/kustomize_v4.5.7_$(OS)_$(ARCH).tar.gz | \ - tar xzf - -C bin/ ;\ - } -else -KUSTOMIZE = $(shell which kustomize) -endif -endif - -.PHONY: ansible-operator -ANSIBLE_OPERATOR = $(shell pwd)/bin/ansible-operator -ansible-operator: ## Download ansible-operator locally if necessary, preferring the $(pwd)/bin path over global if both exist. -ifeq (,$(wildcard $(ANSIBLE_OPERATOR))) -ifeq (,$(shell which ansible-operator 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\ - curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.31.0/ansible-operator_$(OS)_$(ARCH) ;\ - chmod +x $(ANSIBLE_OPERATOR) ;\ - } -else -ANSIBLE_OPERATOR = $(shell which ansible-operator) -endif -endif - -.PHONY: operator-sdk -OPERATOR_SDK ?= ./bin/operator-sdk -operator-sdk: ## Download operator-sdk locally if necessary. -ifeq (,$(wildcard $(OPERATOR_SDK))) -ifeq (, $(shell which operator-sdk 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPERATOR_SDK)) ;\ - curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$(OS)_$(ARCH) ;\ - chmod +x $(OPERATOR_SDK) ;\ - } -else -OPERATOR_SDK = $(shell which operator-sdk) -endif -endif - -.PHONY: bundle -bundle: kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. - $(OPERATOR_SDK) generate kustomize manifests --interactive=false -q - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) - $(OPERATOR_SDK) bundle validate ./bundle - -.PHONY: bundle-build -bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . - -.PHONY: bundle-push -bundle-push: ## Push the bundle image. - $(MAKE) docker-push IMG=$(BUNDLE_IMG) - -.PHONY: opm -OPM = ./bin/opm -opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) -ifeq (,$(shell which opm 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPM)) ;\ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$(OS)-$(ARCH)-opm ;\ - chmod +x $(OPM) ;\ - } -else -OPM = $(shell which opm) -endif -endif - -# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). -# These images MUST exist in a registry and be pull-able. -BUNDLE_IMGS ?= $(BUNDLE_IMG) - -# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). -CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) - -# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. -ifneq ($(origin CATALOG_BASE_IMG), undefined) -FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) -endif - -# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. -# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: -# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator -.PHONY: catalog-build -catalog-build: opm ## Build a catalog image. - $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) - -# Push the catalog image. -.PHONY: catalog-push -catalog-push: ## Push a catalog image. - $(MAKE) docker-push IMG=$(CATALOG_IMG) diff --git a/testdata/ansible/memcached-operator/PROJECT b/testdata/ansible/memcached-operator/PROJECT deleted file mode 100644 index b24bbff..0000000 --- a/testdata/ansible/memcached-operator/PROJECT +++ /dev/null @@ -1,20 +0,0 @@ -# Code generated by tool. DO NOT EDIT. -# This file is used to track the info used to scaffold your project -# and allow the plugins properly work. -# More info: https://book.kubebuilder.io/reference/project-config.html -domain: example.com -layout: -- ansible.sdk.operatorframework.io/v1 -plugins: - manifests.sdk.operatorframework.io/v2: {} - scorecard.sdk.operatorframework.io/v2: {} -projectName: memcached-operator -resources: -- api: - crdVersion: v1 - namespaced: true - domain: example.com - group: cache - kind: Memcached - version: v1alpha1 -version: "3" diff --git a/testdata/ansible/memcached-operator/bundle.Dockerfile b/testdata/ansible/memcached-operator/bundle.Dockerfile deleted file mode 100644 index 432577b..0000000 --- a/testdata/ansible/memcached-operator/bundle.Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM scratch - -# Core bundle labels. -LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 -LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ -LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ -LABEL operators.operatorframework.io.bundle.package.v1=memcached-operator -LABEL operators.operatorframework.io.bundle.channels.v1=alpha - -# Labels for testing. -LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 -LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ - -# Copy files to locations specified by labels. -COPY bundle/manifests /manifests/ -COPY bundle/metadata /metadata/ -COPY bundle/tests/scorecard /tests/scorecard/ diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-monitor_monitoring.coreos.com_v1_servicemonitor.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-monitor_monitoring.coreos.com_v1_servicemonitor.yaml deleted file mode 100644 index e7d0a16..0000000 --- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-monitor_monitoring.coreos.com_v1_servicemonitor.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - app.kubernetes.io/component: metrics - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/instance: controller-manager-metrics-monitor - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: servicemonitor - app.kubernetes.io/part-of: memcached-operator - control-plane: controller-manager - name: memcached-operator-controller-manager-metrics-monitor -spec: - endpoints: - - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - path: /metrics - port: https - scheme: https - tlsConfig: - insecureSkipVerify: true - selector: - matchLabels: - control-plane: controller-manager diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml deleted file mode 100644 index d172662..0000000 --- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-controller-manager-metrics-service_v1_service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: service - app.kubernetes.io/part-of: memcached-operator - control-plane: controller-manager - name: memcached-operator-controller-manager-metrics-service -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - control-plane: controller-manager -status: - loadBalancer: {} diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml deleted file mode 100644 index 6f58e96..0000000 --- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: clusterrole - app.kubernetes.io/part-of: memcached-operator - name: memcached-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get diff --git a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml b/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml deleted file mode 100644 index e1bf0b5..0000000 --- a/testdata/ansible/memcached-operator/bundle/manifests/memcached-operator.clusterserviceversion.yaml +++ /dev/null @@ -1,261 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: |- - [ - { - "apiVersion": "cache.example.com/v1alpha1", - "kind": "Memcached", - "metadata": { - "labels": { - "app.kubernetes.io/created-by": "memcached-operator", - "app.kubernetes.io/instance": "memcached-sample", - "app.kubernetes.io/managed-by": "kustomize", - "app.kubernetes.io/name": "memcached", - "app.kubernetes.io/part-of": "memcached-operator" - }, - "name": "memcached-sample" - }, - "spec": { - "size": 1 - } - } - ] - capabilities: Basic Install - createdAt: "2022-11-08T17:26:37Z" - name: memcached-operator.v0.0.1 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - kind: Memcached - name: memcacheds.cache.example.com - version: v1alpha1 - description: Memcached Operator description. TODO. - displayName: Memcached Operator - icon: - - base64data: "" - mediatype: "" - install: - spec: - clusterPermissions: - - rules: - - apiGroups: - - "" - resources: - - secrets - - pods - - pods/exec - - pods/log - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - cache.example.com - resources: - - memcacheds - - memcacheds/status - - memcacheds/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - serviceAccountName: memcached-operator-controller-manager - deployments: - - label: - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: deployment - app.kubernetes.io/part-of: memcached-operator - control-plane: controller-manager - name: memcached-operator-controller-manager - spec: - replicas: 1 - selector: - matchLabels: - control-plane: controller-manager - strategy: {} - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: manager - labels: - control-plane: controller-manager - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux - containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:6789 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - - --leader-election-id=memcached-operator - env: - - name: ANSIBLE_GATHERING - value: explicit - image: quay.io/example/memcached-operator:v0.0.1 - livenessProbe: - httpGet: - path: /healthz - port: 6789 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 6789 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 768Mi - requests: - cpu: 10m - memory: 256Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - securityContext: - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - serviceAccountName: memcached-operator-controller-manager - terminationGracePeriodSeconds: 10 - permissions: - - rules: - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - serviceAccountName: memcached-operator-controller-manager - strategy: deployment - installModes: - - supported: false - type: OwnNamespace - - supported: false - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - keywords: - - memcached-operator - links: - - name: Memcached Operator - url: https://memcached-operator.domain - maintainers: - - email: your@email.com - name: Maintainer Name - maturity: alpha - provider: - name: Provider Name - url: https://your.domain - version: 0.0.1 diff --git a/testdata/ansible/memcached-operator/bundle/metadata/annotations.yaml b/testdata/ansible/memcached-operator/bundle/metadata/annotations.yaml deleted file mode 100644 index 2a94286..0000000 --- a/testdata/ansible/memcached-operator/bundle/metadata/annotations.yaml +++ /dev/null @@ -1,11 +0,0 @@ -annotations: - # Core bundle annotations. - operators.operatorframework.io.bundle.mediatype.v1: registry+v1 - operators.operatorframework.io.bundle.manifests.v1: manifests/ - operators.operatorframework.io.bundle.metadata.v1: metadata/ - operators.operatorframework.io.bundle.package.v1: memcached-operator - operators.operatorframework.io.bundle.channels.v1: alpha - - # Annotations for testing. - operators.operatorframework.io.test.mediatype.v1: scorecard+v1 - operators.operatorframework.io.test.config.v1: tests/scorecard/ diff --git a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml b/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml deleted file mode 100644 index 0c0690c..0000000 --- a/testdata/ansible/memcached-operator/bundle/tests/scorecard/config.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: scorecard.operatorframework.io/v1alpha3 -kind: Configuration -metadata: - name: config -stages: -- parallel: true - tests: - - entrypoint: - - scorecard-test - - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: basic - test: basic-check-spec-test - storage: - spec: - mountPath: {} - - entrypoint: - - scorecard-test - - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-bundle-validation-test - storage: - spec: - mountPath: {} - - entrypoint: - - scorecard-test - - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-crds-have-validation-test - storage: - spec: - mountPath: {} - - entrypoint: - - scorecard-test - - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-crds-have-resources-test - storage: - spec: - mountPath: {} - - entrypoint: - - scorecard-test - - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-spec-descriptors-test - storage: - spec: - mountPath: {} - - entrypoint: - - scorecard-test - - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-status-descriptors-test - storage: - spec: - mountPath: {} -storage: - spec: - mountPath: {} diff --git a/testdata/ansible/memcached-operator/config/manager/kustomization.yaml b/testdata/ansible/memcached-operator/config/manager/kustomization.yaml deleted file mode 100644 index 1a4048d..0000000 --- a/testdata/ansible/memcached-operator/config/manager/kustomization.yaml +++ /dev/null @@ -1,8 +0,0 @@ -resources: -- manager.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: quay.io/example/memcached-operator - newTag: v0.0.1 diff --git a/testdata/ansible/memcached-operator/config/manifests/bases/memcached-operator.clusterserviceversion.yaml b/testdata/ansible/memcached-operator/config/manifests/bases/memcached-operator.clusterserviceversion.yaml deleted file mode 100644 index 1f1eb42..0000000 --- a/testdata/ansible/memcached-operator/config/manifests/bases/memcached-operator.clusterserviceversion.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: '[]' - capabilities: Basic Install - name: memcached-operator.v0.0.0 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: {} - description: Memcached Operator description. TODO. - displayName: Memcached Operator - icon: - - base64data: "" - mediatype: "" - install: - spec: - deployments: null - strategy: "" - installModes: - - supported: false - type: OwnNamespace - - supported: false - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - keywords: - - memcached-operator - links: - - name: Memcached Operator - url: https://memcached-operator.domain - maintainers: - - email: your@email.com - name: Maintainer Name - maturity: alpha - provider: - name: Provider Name - url: https://your.domain - version: 0.0.0 diff --git a/testdata/ansible/memcached-operator/config/manifests/kustomization.yaml b/testdata/ansible/memcached-operator/config/manifests/kustomization.yaml deleted file mode 100644 index 705a7b9..0000000 --- a/testdata/ansible/memcached-operator/config/manifests/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# These resources constitute the fully configured set of manifests -# used to generate the 'manifests/' directory in a bundle. -resources: -- bases/memcached-operator.clusterserviceversion.yaml -- ../default -- ../samples -- ../scorecard diff --git a/testdata/ansible/memcached-operator/config/rbac/role.yaml b/testdata/ansible/memcached-operator/config/rbac/role.yaml deleted file mode 100644 index 27958f8..0000000 --- a/testdata/ansible/memcached-operator/config/rbac/role.yaml +++ /dev/null @@ -1,57 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: manager-role -rules: - ## - ## Base operator rules - ## - - apiGroups: - - "" - resources: - - secrets - - pods - - pods/exec - - pods/log - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - ## - ## Rules for cache.example.com/v1alpha1, Kind: Memcached - ## - - apiGroups: - - cache.example.com - resources: - - memcacheds - - memcacheds/status - - memcacheds/finalizers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -#+kubebuilder:scaffold:rules diff --git a/testdata/ansible/memcached-operator/config/scorecard/bases/config.yaml b/testdata/ansible/memcached-operator/config/scorecard/bases/config.yaml deleted file mode 100644 index c770478..0000000 --- a/testdata/ansible/memcached-operator/config/scorecard/bases/config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: scorecard.operatorframework.io/v1alpha3 -kind: Configuration -metadata: - name: config -stages: -- parallel: true - tests: [] diff --git a/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml b/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml deleted file mode 100644 index 50cd2d0..0000000 --- a/testdata/ansible/memcached-operator/config/scorecard/kustomization.yaml +++ /dev/null @@ -1,16 +0,0 @@ -resources: -- bases/config.yaml -patchesJson6902: -- path: patches/basic.config.yaml - target: - group: scorecard.operatorframework.io - version: v1alpha3 - kind: Configuration - name: config -- path: patches/olm.config.yaml - target: - group: scorecard.operatorframework.io - version: v1alpha3 - kind: Configuration - name: config -#+kubebuilder:scaffold:patchesJson6902 diff --git a/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml b/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml deleted file mode 100644 index 04b59f3..0000000 --- a/testdata/ansible/memcached-operator/config/scorecard/patches/basic.config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: basic - test: basic-check-spec-test diff --git a/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml b/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml deleted file mode 100644 index 8f1c952..0000000 --- a/testdata/ansible/memcached-operator/config/scorecard/patches/olm.config.yaml +++ /dev/null @@ -1,50 +0,0 @@ -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-bundle-validation-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-crds-have-validation-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-crds-have-resources-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-spec-descriptors-test -- op: add - path: /stages/0/tests/- - value: - entrypoint: - - scorecard-test - - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.31.0 - labels: - suite: olm - test: olm-status-descriptors-test diff --git a/testdata/ansible/memcached-operator/watches.yaml b/testdata/ansible/memcached-operator/watches.yaml deleted file mode 100644 index ec0cb28..0000000 --- a/testdata/ansible/memcached-operator/watches.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# Use the 'create api' subcommand to add watches to this file. -- version: v1alpha1 - group: cache.example.com - kind: Memcached - playbook: playbooks/memcached.yml -#+kubebuilder:scaffold:watch diff --git a/testdata/ansible/memcached-operator/.gitignore b/testdata/memcached-molecule-operator/.gitignore similarity index 100% rename from testdata/ansible/memcached-operator/.gitignore rename to testdata/memcached-molecule-operator/.gitignore diff --git a/testdata/ansible/memcached-operator/Dockerfile b/testdata/memcached-molecule-operator/Dockerfile similarity index 81% rename from testdata/ansible/memcached-operator/Dockerfile rename to testdata/memcached-molecule-operator/Dockerfile index fe7cfbf..dc47781 100644 --- a/testdata/ansible/memcached-operator/Dockerfile +++ b/testdata/memcached-molecule-operator/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/operator-framework/ansible-operator:v1.31.0 +FROM quay.io/operator-framework/ansible-operator:dev COPY requirements.yml ${HOME}/requirements.yml RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ diff --git a/testdata/memcached-molecule-operator/Makefile b/testdata/memcached-molecule-operator/Makefile new file mode 100644 index 0000000..b9bdc5e --- /dev/null +++ b/testdata/memcached-molecule-operator/Makefile @@ -0,0 +1,109 @@ + +# Image URL to use all building/pushing image targets +IMG ?= controller:latest + +.PHONY: all +all: docker-build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Build + +.PHONY: run +ANSIBLE_ROLES_PATH?="$(shell pwd)/roles" +run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config + $(ANSIBLE_OPERATOR) run + +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + docker build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + docker push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ +# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> than the export will fail) +# To properly provided solutions that supports more than one platform you should use this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: test ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - docker buildx create --name project-v3-builder + docker buildx use project-v3-builder + - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - docker buildx rm project-v3-builder + rm Dockerfile.cross + +##@ Deployment + +.PHONY: install +install: kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl apply -f - + +.PHONY: uninstall +uninstall: kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl delete -f - + +.PHONY: deploy +deploy: kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | kubectl apply -f - + +.PHONY: undeploy +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/default | kubectl delete -f - + +OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') +ARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') + +.PHONY: kustomize +KUSTOMIZE = $(shell pwd)/bin/kustomize +kustomize: ## Download kustomize locally if necessary. +ifeq (,$(wildcard $(KUSTOMIZE))) +ifeq (,$(shell which kustomize 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(KUSTOMIZE)) ;\ + curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.7/kustomize_v4.5.7_$(OS)_$(ARCH).tar.gz | \ + tar xzf - -C bin/ ;\ + } +else +KUSTOMIZE = $(shell which kustomize) +endif +endif + +.PHONY: ansible-operator +ANSIBLE_OPERATOR = $(shell pwd)/bin/ansible-operator +ansible-operator: ## Download ansible-operator locally if necessary, preferring the $(pwd)/bin path over global if both exist. +ifeq (,$(wildcard $(ANSIBLE_OPERATOR))) +ifeq (,$(shell which ansible-operator 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\ + curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/ansible-operator-plugins/releases/download/v1.31.0/ansible-operator_$(OS)_$(ARCH) ;\ + chmod +x $(ANSIBLE_OPERATOR) ;\ + } +else +ANSIBLE_OPERATOR = $(shell which ansible-operator) +endif +endif diff --git a/testdata/memcached-molecule-operator/PROJECT b/testdata/memcached-molecule-operator/PROJECT new file mode 100644 index 0000000..f1aba08 --- /dev/null +++ b/testdata/memcached-molecule-operator/PROJECT @@ -0,0 +1,39 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: example.com +layout: +- go.kubebuilder.io/v1 +multigroup: true +projectName: memcached-molecule-operator +resources: +- api: + crdVersion: v1 + namespaced: true + domain: example.com + group: cache + kind: Memcached + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: example.com + group: cache + kind: Foo + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: example.com + group: cache + kind: Memfin + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: example.com + group: ignore + kind: Secret + version: v1 +version: "3" diff --git a/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_foos.yaml b/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_foos.yaml new file mode 100644 index 0000000..6ae7ea9 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_foos.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: foos.cache.example.com +spec: + group: cache.example.com + names: + kind: Foo + listKind: FooList + plural: foos + singular: foo + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Foo is the Schema for the foos API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Foo + type: object + x-kubernetes-preserve-unknown-fields: true + status: + description: Status defines the observed state of Foo + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: + status: {} diff --git a/testdata/ansible/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml b/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_memcacheds.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/crd/bases/cache.example.com_memcacheds.yaml rename to testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_memcacheds.yaml diff --git a/testdata/ansible/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml b/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_memfins.yaml similarity index 77% rename from testdata/ansible/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml rename to testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_memfins.yaml index 9a95005..f872183 100644 --- a/testdata/ansible/memcached-operator/bundle/manifests/cache.example.com_memcacheds.yaml +++ b/testdata/memcached-molecule-operator/config/crd/bases/cache.example.com_memfins.yaml @@ -1,21 +1,21 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - creationTimestamp: null - name: memcacheds.cache.example.com + name: memfins.cache.example.com spec: group: cache.example.com names: - kind: Memcached - listKind: MemcachedList - plural: memcacheds - singular: memcached + kind: Memfin + listKind: MemfinList + plural: memfins + singular: memfin scope: Namespaced versions: - name: v1alpha1 schema: openAPIV3Schema: - description: Memcached is the Schema for the memcacheds API + description: Memfin is the Schema for the memfins API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -30,11 +30,11 @@ spec: metadata: type: object spec: - description: Spec defines the desired state of Memcached + description: Spec defines the desired state of Memfin type: object x-kubernetes-preserve-unknown-fields: true status: - description: Status defines the observed state of Memcached + description: Status defines the observed state of Memfin type: object x-kubernetes-preserve-unknown-fields: true type: object @@ -42,9 +42,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/testdata/memcached-molecule-operator/config/crd/bases/ignore.example.com_secrets.yaml b/testdata/memcached-molecule-operator/config/crd/bases/ignore.example.com_secrets.yaml new file mode 100644 index 0000000..68dff70 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/crd/bases/ignore.example.com_secrets.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: secrets.ignore.example.com +spec: + group: ignore.example.com + names: + kind: Secret + listKind: SecretList + plural: secrets + singular: secret + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: Secret is the Schema for the secrets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Secret + type: object + x-kubernetes-preserve-unknown-fields: true + status: + description: Status defines the observed state of Secret + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + served: true + storage: true + subresources: + status: {} diff --git a/testdata/ansible/memcached-operator/config/crd/kustomization.yaml b/testdata/memcached-molecule-operator/config/crd/kustomization.yaml similarity index 71% rename from testdata/ansible/memcached-operator/config/crd/kustomization.yaml rename to testdata/memcached-molecule-operator/config/crd/kustomization.yaml index c8033c3..a72b764 100644 --- a/testdata/ansible/memcached-operator/config/crd/kustomization.yaml +++ b/testdata/memcached-molecule-operator/config/crd/kustomization.yaml @@ -3,4 +3,7 @@ # It should be run by config/default resources: - bases/cache.example.com_memcacheds.yaml +- bases/cache.example.com_foos.yaml +- bases/cache.example.com_memfins.yaml +- bases/ignore.example.com_secrets.yaml #+kubebuilder:scaffold:crdkustomizeresource diff --git a/testdata/ansible/memcached-operator/config/default/kustomization.yaml b/testdata/memcached-molecule-operator/config/default/kustomization.yaml similarity index 89% rename from testdata/ansible/memcached-operator/config/default/kustomization.yaml rename to testdata/memcached-molecule-operator/config/default/kustomization.yaml index 5850b40..133d093 100644 --- a/testdata/ansible/memcached-operator/config/default/kustomization.yaml +++ b/testdata/memcached-molecule-operator/config/default/kustomization.yaml @@ -1,12 +1,12 @@ # Adds namespace to all resources. -namespace: memcached-operator-system +namespace: memcached-molecule-operator-system # Value of this field is prepended to the # names of all resources, e.g. a deployment named # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: memcached-operator- +namePrefix: memcached-molecule-operator- # Labels to add to all resources and selectors. #labels: diff --git a/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml b/testdata/memcached-molecule-operator/config/default/manager_auth_proxy_patch.yaml similarity index 96% rename from testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml rename to testdata/memcached-molecule-operator/config/default/manager_auth_proxy_patch.yaml index 1f25756..1b1d337 100644 --- a/testdata/ansible/memcached-operator/config/default/manager_auth_proxy_patch.yaml +++ b/testdata/memcached-molecule-operator/config/default/manager_auth_proxy_patch.yaml @@ -53,4 +53,4 @@ spec: - "--health-probe-bind-address=:6789" - "--metrics-bind-address=127.0.0.1:8080" - "--leader-elect" - - "--leader-election-id=memcached-operator" + - "--leader-election-id=memcached-molecule-operator" diff --git a/testdata/ansible/memcached-operator/config/default/manager_config_patch.yaml b/testdata/memcached-molecule-operator/config/default/manager_config_patch.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/default/manager_config_patch.yaml rename to testdata/memcached-molecule-operator/config/default/manager_config_patch.yaml diff --git a/testdata/memcached-molecule-operator/config/manager/kustomization.yaml b/testdata/memcached-molecule-operator/config/manager/kustomization.yaml new file mode 100644 index 0000000..5c5f0b8 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/testdata/ansible/memcached-operator/config/manager/manager.yaml b/testdata/memcached-molecule-operator/config/manager/manager.yaml similarity index 76% rename from testdata/ansible/memcached-operator/config/manager/manager.yaml rename to testdata/memcached-molecule-operator/config/manager/manager.yaml index eec8fc3..eac55b5 100644 --- a/testdata/ansible/memcached-operator/config/manager/manager.yaml +++ b/testdata/memcached-molecule-operator/config/manager/manager.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: namespace app.kubernetes.io/instance: system app.kubernetes.io/component: manager - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: system --- @@ -21,8 +21,8 @@ metadata: app.kubernetes.io/name: deployment app.kubernetes.io/instance: controller-manager app.kubernetes.io/component: manager - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize spec: selector: @@ -58,12 +58,17 @@ spec: # - linux securityContext: runAsNonRoot: true - seccompProfile: - type: RuntimeDefault + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault containers: - args: - --leader-elect - - --leader-election-id=memcached-operator + - --leader-election-id=memcached-molecule-operator image: controller:latest name: manager env: diff --git a/testdata/ansible/memcached-operator/config/prometheus/kustomization.yaml b/testdata/memcached-molecule-operator/config/prometheus/kustomization.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/prometheus/kustomization.yaml rename to testdata/memcached-molecule-operator/config/prometheus/kustomization.yaml diff --git a/testdata/ansible/memcached-operator/config/prometheus/monitor.yaml b/testdata/memcached-molecule-operator/config/prometheus/monitor.yaml similarity index 85% rename from testdata/ansible/memcached-operator/config/prometheus/monitor.yaml rename to testdata/memcached-molecule-operator/config/prometheus/monitor.yaml index 4dfab6b..cf3cd4d 100644 --- a/testdata/ansible/memcached-operator/config/prometheus/monitor.yaml +++ b/testdata/memcached-molecule-operator/config/prometheus/monitor.yaml @@ -8,8 +8,8 @@ metadata: app.kubernetes.io/name: servicemonitor app.kubernetes.io/instance: controller-manager-metrics-monitor app.kubernetes.io/component: metrics - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-monitor namespace: system diff --git a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_client_clusterrole.yaml b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_client_clusterrole.yaml similarity index 73% rename from testdata/ansible/memcached-operator/config/rbac/auth_proxy_client_clusterrole.yaml rename to testdata/memcached-molecule-operator/config/rbac/auth_proxy_client_clusterrole.yaml index a2a3b44..297d52d 100644 --- a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_client_clusterrole.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_client_clusterrole.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: clusterrole app.kubernetes.io/instance: metrics-reader app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: metrics-reader rules: diff --git a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_role.yaml b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_role.yaml similarity index 79% rename from testdata/ansible/memcached-operator/config/rbac/auth_proxy_role.yaml rename to testdata/memcached-molecule-operator/config/rbac/auth_proxy_role.yaml index b7c9c6f..c52a577 100644 --- a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_role.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_role.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: clusterrole app.kubernetes.io/instance: proxy-role app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: proxy-role rules: diff --git a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_role_binding.yaml b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_role_binding.yaml similarity index 79% rename from testdata/ansible/memcached-operator/config/rbac/auth_proxy_role_binding.yaml rename to testdata/memcached-molecule-operator/config/rbac/auth_proxy_role_binding.yaml index a4a1a85..17100ed 100644 --- a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_role_binding.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_role_binding.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: clusterrolebinding app.kubernetes.io/instance: proxy-rolebinding app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: proxy-rolebinding roleRef: diff --git a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_service.yaml similarity index 79% rename from testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml rename to testdata/memcached-molecule-operator/config/rbac/auth_proxy_service.yaml index ecaef24..e67cda4 100644 --- a/testdata/ansible/memcached-operator/config/rbac/auth_proxy_service.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/auth_proxy_service.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: service app.kubernetes.io/instance: controller-manager-metrics-service app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-service namespace: system diff --git a/testdata/memcached-molecule-operator/config/rbac/foo_editor_role.yaml b/testdata/memcached-molecule-operator/config/rbac/foo_editor_role.yaml new file mode 100644 index 0000000..5ec5b21 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/foo_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit foos. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: foo-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: foo-editor-role +rules: +- apiGroups: + - cache.example.com + resources: + - foos + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cache.example.com + resources: + - foos/status + verbs: + - get diff --git a/testdata/memcached-molecule-operator/config/rbac/foo_viewer_role.yaml b/testdata/memcached-molecule-operator/config/rbac/foo_viewer_role.yaml new file mode 100644 index 0000000..5b8540d --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/foo_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view foos. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: foo-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: foo-viewer-role +rules: +- apiGroups: + - cache.example.com + resources: + - foos + verbs: + - get + - list + - watch +- apiGroups: + - cache.example.com + resources: + - foos/status + verbs: + - get diff --git a/testdata/memcached-molecule-operator/config/rbac/ignore_secret_editor_role.yaml b/testdata/memcached-molecule-operator/config/rbac/ignore_secret_editor_role.yaml new file mode 100644 index 0000000..c9c7f3c --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/ignore_secret_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit secrets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: secret-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: secret-editor-role +rules: +- apiGroups: + - ignore.example.com + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ignore.example.com + resources: + - secrets/status + verbs: + - get diff --git a/testdata/memcached-molecule-operator/config/rbac/ignore_secret_viewer_role.yaml b/testdata/memcached-molecule-operator/config/rbac/ignore_secret_viewer_role.yaml new file mode 100644 index 0000000..46a0bb4 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/ignore_secret_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view secrets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: secret-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: secret-viewer-role +rules: +- apiGroups: + - ignore.example.com + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - ignore.example.com + resources: + - secrets/status + verbs: + - get diff --git a/testdata/ansible/memcached-operator/config/rbac/kustomization.yaml b/testdata/memcached-molecule-operator/config/rbac/kustomization.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/rbac/kustomization.yaml rename to testdata/memcached-molecule-operator/config/rbac/kustomization.yaml diff --git a/testdata/ansible/memcached-operator/config/rbac/leader_election_role.yaml b/testdata/memcached-molecule-operator/config/rbac/leader_election_role.yaml similarity index 84% rename from testdata/ansible/memcached-operator/config/rbac/leader_election_role.yaml rename to testdata/memcached-molecule-operator/config/rbac/leader_election_role.yaml index ce7088e..2b2bc6e 100644 --- a/testdata/ansible/memcached-operator/config/rbac/leader_election_role.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/leader_election_role.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: role app.kubernetes.io/instance: leader-election-role app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: leader-election-role rules: diff --git a/testdata/ansible/memcached-operator/config/rbac/leader_election_role_binding.yaml b/testdata/memcached-molecule-operator/config/rbac/leader_election_role_binding.yaml similarity index 79% rename from testdata/ansible/memcached-operator/config/rbac/leader_election_role_binding.yaml rename to testdata/memcached-molecule-operator/config/rbac/leader_election_role_binding.yaml index 4ee27a3..396ceeb 100644 --- a/testdata/ansible/memcached-operator/config/rbac/leader_election_role_binding.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/leader_election_role_binding.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: rolebinding app.kubernetes.io/instance: leader-election-rolebinding app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: leader-election-rolebinding roleRef: diff --git a/testdata/ansible/memcached-operator/config/rbac/memcached_editor_role.yaml b/testdata/memcached-molecule-operator/config/rbac/memcached_editor_role.yaml similarity index 82% rename from testdata/ansible/memcached-operator/config/rbac/memcached_editor_role.yaml rename to testdata/memcached-molecule-operator/config/rbac/memcached_editor_role.yaml index cbc4577..52d84bb 100644 --- a/testdata/ansible/memcached-operator/config/rbac/memcached_editor_role.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/memcached_editor_role.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: clusterrole app.kubernetes.io/instance: memcached-editor-role app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: memcached-editor-role rules: diff --git a/testdata/ansible/memcached-operator/config/rbac/memcached_viewer_role.yaml b/testdata/memcached-molecule-operator/config/rbac/memcached_viewer_role.yaml similarity index 81% rename from testdata/ansible/memcached-operator/config/rbac/memcached_viewer_role.yaml rename to testdata/memcached-molecule-operator/config/rbac/memcached_viewer_role.yaml index b8977ec..ff6215c 100644 --- a/testdata/ansible/memcached-operator/config/rbac/memcached_viewer_role.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/memcached_viewer_role.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: clusterrole app.kubernetes.io/instance: memcached-viewer-role app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: memcached-viewer-role rules: diff --git a/testdata/memcached-molecule-operator/config/rbac/memfin_editor_role.yaml b/testdata/memcached-molecule-operator/config/rbac/memfin_editor_role.yaml new file mode 100644 index 0000000..25e7ca4 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/memfin_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit memfins. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: memfin-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: memfin-editor-role +rules: +- apiGroups: + - cache.example.com + resources: + - memfins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cache.example.com + resources: + - memfins/status + verbs: + - get diff --git a/testdata/memcached-molecule-operator/config/rbac/memfin_viewer_role.yaml b/testdata/memcached-molecule-operator/config/rbac/memfin_viewer_role.yaml new file mode 100644 index 0000000..458e917 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/memfin_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view memfins. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: memfin-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + name: memfin-viewer-role +rules: +- apiGroups: + - cache.example.com + resources: + - memfins + verbs: + - get + - list + - watch +- apiGroups: + - cache.example.com + resources: + - memfins/status + verbs: + - get diff --git a/testdata/memcached-molecule-operator/config/rbac/role.yaml b/testdata/memcached-molecule-operator/config/rbac/role.yaml new file mode 100644 index 0000000..3289d44 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/rbac/role.yaml @@ -0,0 +1,126 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: + ## + ## Base operator rules + ## + - apiGroups: + - "" + resources: + - secrets + - pods + - pods/exec + - pods/log + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + ## + ## Rules for cache.example.com/v1alpha1, Kind: Memcached + ## + - apiGroups: + - cache.example.com + resources: + - memcacheds + - memcacheds/status + - memcacheds/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + ## + ## Rules for cache.example.com/v1alpha1, Kind: Foo + ## + - apiGroups: + - cache.example.com + resources: + - foos + - foos/status + - foos/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + ## + ## Rules for cache.example.com/v1alpha1, Kind: Memfin + ## + - apiGroups: + - cache.example.com + resources: + - memfins + - memfins/status + - memfins/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + ## + ## Rules for ignore.example.com/v1, Kind: Secret + ## + - apiGroups: + - ignore.example.com + resources: + - secrets + - secrets/status + - secrets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + + ## + ## Apply customize roles for base operator + ## + - apiGroups: + - "" + resources: + - configmaps + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +#+kubebuilder:scaffold:rules + diff --git a/testdata/ansible/memcached-operator/config/rbac/role_binding.yaml b/testdata/memcached-molecule-operator/config/rbac/role_binding.yaml similarity index 79% rename from testdata/ansible/memcached-operator/config/rbac/role_binding.yaml rename to testdata/memcached-molecule-operator/config/rbac/role_binding.yaml index cc2b714..4a8a546 100644 --- a/testdata/ansible/memcached-operator/config/rbac/role_binding.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/role_binding.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: clusterrolebinding app.kubernetes.io/instance: manager-rolebinding app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: manager-rolebinding roleRef: diff --git a/testdata/ansible/memcached-operator/config/rbac/service_account.yaml b/testdata/memcached-molecule-operator/config/rbac/service_account.yaml similarity index 69% rename from testdata/ansible/memcached-operator/config/rbac/service_account.yaml rename to testdata/memcached-molecule-operator/config/rbac/service_account.yaml index 9712cf0..381d3d8 100644 --- a/testdata/ansible/memcached-operator/config/rbac/service_account.yaml +++ b/testdata/memcached-molecule-operator/config/rbac/service_account.yaml @@ -5,8 +5,8 @@ metadata: app.kubernetes.io/name: serviceaccount app.kubernetes.io/instance: controller-manager-sa app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: memcached-operator - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize name: controller-manager namespace: system diff --git a/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_foo.yaml b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_foo.yaml new file mode 100644 index 0000000..f68f688 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_foo.yaml @@ -0,0 +1,12 @@ +apiVersion: cache.example.com/v1alpha1 +kind: Foo +metadata: + labels: + app.kubernetes.io/name: foo + app.kubernetes.io/instance: foo-sample + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: memcached-molecule-operator + name: foo-sample +spec: + foo: bar diff --git a/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memcached.yaml similarity index 67% rename from testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml rename to testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memcached.yaml index 1a37784..8bd4ce0 100644 --- a/testdata/ansible/memcached-operator/config/samples/cache_v1alpha1_memcached.yaml +++ b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memcached.yaml @@ -4,9 +4,9 @@ metadata: labels: app.kubernetes.io/name: memcached app.kubernetes.io/instance: memcached-sample - app.kubernetes.io/part-of: memcached-operator + app.kubernetes.io/part-of: memcached-molecule-operator app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: memcached-operator + app.kubernetes.io/created-by: memcached-molecule-operator name: memcached-sample spec: size: 1 diff --git a/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memfin.yaml b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memfin.yaml new file mode 100644 index 0000000..f67ee54 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/samples/cache_v1alpha1_memfin.yaml @@ -0,0 +1,12 @@ +apiVersion: cache.example.com/v1alpha1 +kind: Memfin +metadata: + labels: + app.kubernetes.io/name: memfin + app.kubernetes.io/instance: memfin-sample + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: memcached-molecule-operator + name: memfin-sample +spec: + foo: bar diff --git a/testdata/memcached-molecule-operator/config/samples/ignore_v1_secret.yaml b/testdata/memcached-molecule-operator/config/samples/ignore_v1_secret.yaml new file mode 100644 index 0000000..44e7427 --- /dev/null +++ b/testdata/memcached-molecule-operator/config/samples/ignore_v1_secret.yaml @@ -0,0 +1,12 @@ +apiVersion: ignore.example.com/v1 +kind: Secret +metadata: + labels: + app.kubernetes.io/name: secret + app.kubernetes.io/instance: secret-sample + app.kubernetes.io/part-of: memcached-molecule-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: memcached-molecule-operator + name: secret-sample +spec: + # TODO(user): Add fields here diff --git a/testdata/ansible/memcached-operator/config/samples/kustomization.yaml b/testdata/memcached-molecule-operator/config/samples/kustomization.yaml similarity index 62% rename from testdata/ansible/memcached-operator/config/samples/kustomization.yaml rename to testdata/memcached-molecule-operator/config/samples/kustomization.yaml index 89d9111..26c1898 100644 --- a/testdata/ansible/memcached-operator/config/samples/kustomization.yaml +++ b/testdata/memcached-molecule-operator/config/samples/kustomization.yaml @@ -1,4 +1,7 @@ ## Append samples of your project ## resources: - cache_v1alpha1_memcached.yaml +- cache_v1alpha1_foo.yaml +- cache_v1alpha1_memfin.yaml +- ignore_v1_secret.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/testdata/ansible/memcached-operator/config/testing/debug_logs_patch.yaml b/testdata/memcached-molecule-operator/config/testing/debug_logs_patch.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/testing/debug_logs_patch.yaml rename to testdata/memcached-molecule-operator/config/testing/debug_logs_patch.yaml diff --git a/testdata/ansible/memcached-operator/config/testing/kustomization.yaml b/testdata/memcached-molecule-operator/config/testing/kustomization.yaml similarity index 93% rename from testdata/ansible/memcached-operator/config/testing/kustomization.yaml rename to testdata/memcached-molecule-operator/config/testing/kustomization.yaml index 4109162..1267637 100644 --- a/testdata/ansible/memcached-operator/config/testing/kustomization.yaml +++ b/testdata/memcached-molecule-operator/config/testing/kustomization.yaml @@ -8,6 +8,7 @@ namePrefix: osdk- # someName: someValue patchesStrategicMerge: +- watch_namespace_patch.yaml - manager_image.yaml - debug_logs_patch.yaml - ../default/manager_auth_proxy_patch.yaml diff --git a/testdata/ansible/memcached-operator/config/testing/manager_image.yaml b/testdata/memcached-molecule-operator/config/testing/manager_image.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/testing/manager_image.yaml rename to testdata/memcached-molecule-operator/config/testing/manager_image.yaml diff --git a/testdata/ansible/memcached-operator/config/testing/pull_policy/Always.yaml b/testdata/memcached-molecule-operator/config/testing/pull_policy/Always.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/testing/pull_policy/Always.yaml rename to testdata/memcached-molecule-operator/config/testing/pull_policy/Always.yaml diff --git a/testdata/ansible/memcached-operator/config/testing/pull_policy/IfNotPresent.yaml b/testdata/memcached-molecule-operator/config/testing/pull_policy/IfNotPresent.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/testing/pull_policy/IfNotPresent.yaml rename to testdata/memcached-molecule-operator/config/testing/pull_policy/IfNotPresent.yaml diff --git a/testdata/ansible/memcached-operator/config/testing/pull_policy/Never.yaml b/testdata/memcached-molecule-operator/config/testing/pull_policy/Never.yaml similarity index 100% rename from testdata/ansible/memcached-operator/config/testing/pull_policy/Never.yaml rename to testdata/memcached-molecule-operator/config/testing/pull_policy/Never.yaml diff --git a/testdata/memcached-molecule-operator/config/testing/watch_namespace_patch.yaml b/testdata/memcached-molecule-operator/config/testing/watch_namespace_patch.yaml new file mode 100644 index 0000000..21da27e --- /dev/null +++ b/testdata/memcached-molecule-operator/config/testing/watch_namespace_patch.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace diff --git a/testdata/ansible/memcached-operator/molecule/default/converge.yml b/testdata/memcached-molecule-operator/molecule/default/converge.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/converge.yml rename to testdata/memcached-molecule-operator/molecule/default/converge.yml diff --git a/testdata/ansible/memcached-operator/molecule/default/create.yml b/testdata/memcached-molecule-operator/molecule/default/create.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/create.yml rename to testdata/memcached-molecule-operator/molecule/default/create.yml diff --git a/testdata/ansible/memcached-operator/molecule/default/destroy.yml b/testdata/memcached-molecule-operator/molecule/default/destroy.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/destroy.yml rename to testdata/memcached-molecule-operator/molecule/default/destroy.yml diff --git a/testdata/ansible/memcached-operator/molecule/default/kustomize.yml b/testdata/memcached-molecule-operator/molecule/default/kustomize.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/kustomize.yml rename to testdata/memcached-molecule-operator/molecule/default/kustomize.yml diff --git a/testdata/ansible/memcached-operator/molecule/default/molecule.yml b/testdata/memcached-molecule-operator/molecule/default/molecule.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/molecule.yml rename to testdata/memcached-molecule-operator/molecule/default/molecule.yml diff --git a/testdata/ansible/memcached-operator/molecule/default/prepare.yml b/testdata/memcached-molecule-operator/molecule/default/prepare.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/prepare.yml rename to testdata/memcached-molecule-operator/molecule/default/prepare.yml diff --git a/testdata/memcached-molecule-operator/molecule/default/tasks/foo_test.yml b/testdata/memcached-molecule-operator/molecule/default/tasks/foo_test.yml new file mode 100644 index 0000000..49462a0 --- /dev/null +++ b/testdata/memcached-molecule-operator/molecule/default/tasks/foo_test.yml @@ -0,0 +1,13 @@ +--- +- name: Create the cache.example.com/v1alpha1.Foo + k8s: + state: present + namespace: '{{ namespace }}' + definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" + wait: yes + wait_timeout: 300 + wait_condition: + type: Successful + status: "True" + vars: + cr_file: 'cache_v1alpha1_foo.yaml' diff --git a/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml b/testdata/memcached-molecule-operator/molecule/default/tasks/memcached_test.yml similarity index 58% rename from testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml rename to testdata/memcached-molecule-operator/molecule/default/tasks/memcached_test.yml index b13cac0..bc9511b 100644 --- a/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml +++ b/testdata/memcached-molecule-operator/molecule/default/tasks/memcached_test.yml @@ -33,6 +33,17 @@ namespace=namespace, label_selector="app=memcached" )}}' +- name: Create ConfigMap that the Operator should delete + k8s: + definition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: deleteme + namespace: '{{ namespace }}' + data: + delete: me + - name: Verify custom status exists assert: @@ -45,6 +56,66 @@ resource_name=custom_resource.metadata.name )}}' +# This will verify that the secret role was executed +- name: Verify that test-service was created + assert: + that: lookup('k8s', kind='Service', api_version='v1', namespace=namespace, resource_name='test-service') + + +- name: Verify that project testing-foo was created + assert: + that: lookup('k8s', kind='Namespace', api_version='v1', resource_name='testing-foo') + when: "'project.openshift.io' in lookup('k8s', cluster_info='api_groups')" + +- name: Search for all running pods + kubernetes.core.k8s_info: + kind: Pod + label_selectors: + - "control-plane = controller-manager" + register: output +- name: Curl the metrics from the manager + kubernetes.core.k8s_exec: + namespace: default + container: manager + pod: "{{ output.resources[0].metadata.name }}" + command: curl localhost:8080/metrics + register: metrics_output + +- name: Assert sanity metrics were created + assert: + that: + - "'sanity_counter 0' in metrics_output.stdout" + - "'sanity_gauge 0' in metrics_output.stdout" + - "'sanity_histogram_bucket' in metrics_output.stdout" + - "'sanity_summary summary' in metrics_output.stdout" + +- name: Assert Counter works as expected + assert: + that: + - "'counter_inc_test 1' in metrics_output.stdout" + - "'counter_add_test 2' in metrics_output.stdout" + +- name: Assert Gauge works as expected + assert: + that: + - "'gauge_set_test 5' in metrics_output.stdout" + - "'gauge_add_test 7' in metrics_output.stdout" + - "'gauge_sub_test -7' in metrics_output.stdout" + # result is epoch time in seconds so the first digit is good until 2033 + - "'gauge_time_test 1' in metrics_output.stdout" + +- name: Assert Summary works as expected + assert: + that: + - "'summary_test_sum 2' in metrics_output.stdout" + +- name: Assert Histogram works as expected + assert: + that: + - "'histogram_test_sum 2' in metrics_output.stdout" + + + - when: molecule_yml.scenario.name == "test-local" block: - name: Restart the operator by killing the pod @@ -57,7 +128,7 @@ namespace: '{{ namespace }}' name: '{{ pod.metadata.name }}' vars: - pod: '{{ q("k8s", api_version="v1", kind="Pod", namespace=namespace, label_selector="name=memcached-operator").0 }}' + pod: '{{ q("k8s", api_version="v1", kind="Pod", namespace=namespace, label_selector="name=memcached-molecule-operator").0 }}' - name: Wait 2 minutes for operator deployment debug: @@ -74,7 +145,7 @@ kind="Deployment", api_version="apps/v1", namespace=namespace, - resource_name="memcached-operator" + resource_name="memcached-molecule-operator" )}}' - name: Wait for reconciliation to have a chance at finishing diff --git a/testdata/memcached-molecule-operator/molecule/default/tasks/memfin_test.yml b/testdata/memcached-molecule-operator/molecule/default/tasks/memfin_test.yml new file mode 100644 index 0000000..c7647de --- /dev/null +++ b/testdata/memcached-molecule-operator/molecule/default/tasks/memfin_test.yml @@ -0,0 +1,13 @@ +--- +- name: Create the cache.example.com/v1alpha1.Memfin + k8s: + state: present + namespace: '{{ namespace }}' + definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" + wait: yes + wait_timeout: 300 + wait_condition: + type: Successful + status: "True" + vars: + cr_file: 'cache_v1alpha1_memfin.yaml' diff --git a/testdata/ansible/memcached-operator/molecule/default/verify.yml b/testdata/memcached-molecule-operator/molecule/default/verify.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/default/verify.yml rename to testdata/memcached-molecule-operator/molecule/default/verify.yml diff --git a/testdata/ansible/memcached-operator/molecule/kind/converge.yml b/testdata/memcached-molecule-operator/molecule/kind/converge.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/kind/converge.yml rename to testdata/memcached-molecule-operator/molecule/kind/converge.yml diff --git a/testdata/ansible/memcached-operator/molecule/kind/create.yml b/testdata/memcached-molecule-operator/molecule/kind/create.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/kind/create.yml rename to testdata/memcached-molecule-operator/molecule/kind/create.yml diff --git a/testdata/ansible/memcached-operator/molecule/kind/destroy.yml b/testdata/memcached-molecule-operator/molecule/kind/destroy.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/kind/destroy.yml rename to testdata/memcached-molecule-operator/molecule/kind/destroy.yml diff --git a/testdata/ansible/memcached-operator/molecule/kind/molecule.yml b/testdata/memcached-molecule-operator/molecule/kind/molecule.yml similarity index 100% rename from testdata/ansible/memcached-operator/molecule/kind/molecule.yml rename to testdata/memcached-molecule-operator/molecule/kind/molecule.yml diff --git a/testdata/ansible/memcached-operator/playbooks/.placeholder b/testdata/memcached-molecule-operator/playbooks/.placeholder similarity index 100% rename from testdata/ansible/memcached-operator/playbooks/.placeholder rename to testdata/memcached-molecule-operator/playbooks/.placeholder diff --git a/testdata/memcached-molecule-operator/playbooks/foo.yml b/testdata/memcached-molecule-operator/playbooks/foo.yml new file mode 100644 index 0000000..f78af26 --- /dev/null +++ b/testdata/memcached-molecule-operator/playbooks/foo.yml @@ -0,0 +1,9 @@ +--- +- hosts: localhost + gather_facts: no + collections: + - kubernetes.core + - operator_sdk.util + tasks: + - import_role: + name: "foo" diff --git a/testdata/ansible/memcached-operator/playbooks/memcached.yml b/testdata/memcached-molecule-operator/playbooks/memcached.yml similarity index 100% rename from testdata/ansible/memcached-operator/playbooks/memcached.yml rename to testdata/memcached-molecule-operator/playbooks/memcached.yml diff --git a/testdata/memcached-molecule-operator/playbooks/memfin.yml b/testdata/memcached-molecule-operator/playbooks/memfin.yml new file mode 100644 index 0000000..35cc838 --- /dev/null +++ b/testdata/memcached-molecule-operator/playbooks/memfin.yml @@ -0,0 +1,9 @@ +--- +- hosts: localhost + gather_facts: no + collections: + - kubernetes.core + - operator_sdk.util + tasks: + - import_role: + name: "memfin" diff --git a/testdata/ansible/memcached-operator/requirements.yml b/testdata/memcached-molecule-operator/requirements.yml similarity index 100% rename from testdata/ansible/memcached-operator/requirements.yml rename to testdata/memcached-molecule-operator/requirements.yml diff --git a/testdata/ansible/memcached-operator/roles/.placeholder b/testdata/memcached-molecule-operator/roles/.placeholder similarity index 100% rename from testdata/ansible/memcached-operator/roles/.placeholder rename to testdata/memcached-molecule-operator/roles/.placeholder diff --git a/testdata/ansible/memcached-operator/roles/memcached/README.md b/testdata/memcached-molecule-operator/roles/foo/README.md similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/README.md rename to testdata/memcached-molecule-operator/roles/foo/README.md diff --git a/testdata/memcached-molecule-operator/roles/foo/defaults/main.yml b/testdata/memcached-molecule-operator/roles/foo/defaults/main.yml new file mode 100644 index 0000000..8cd473d --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/foo/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for Foo diff --git a/testdata/ansible/memcached-operator/roles/memcached/files/.placeholder b/testdata/memcached-molecule-operator/roles/foo/files/.placeholder similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/files/.placeholder rename to testdata/memcached-molecule-operator/roles/foo/files/.placeholder diff --git a/testdata/memcached-molecule-operator/roles/foo/handlers/main.yml b/testdata/memcached-molecule-operator/roles/foo/handlers/main.yml new file mode 100644 index 0000000..7d1a0b3 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/foo/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for Foo diff --git a/testdata/ansible/memcached-operator/roles/memcached/meta/main.yml b/testdata/memcached-molecule-operator/roles/foo/meta/main.yml similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/meta/main.yml rename to testdata/memcached-molecule-operator/roles/foo/meta/main.yml diff --git a/testdata/memcached-molecule-operator/roles/foo/tasks/main.yml b/testdata/memcached-molecule-operator/roles/foo/tasks/main.yml new file mode 100644 index 0000000..3b5a8af --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/foo/tasks/main.yml @@ -0,0 +1,2 @@ +--- +# tasks file for Foo diff --git a/testdata/ansible/memcached-operator/roles/memcached/templates/.placeholder b/testdata/memcached-molecule-operator/roles/foo/templates/.placeholder similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/templates/.placeholder rename to testdata/memcached-molecule-operator/roles/foo/templates/.placeholder diff --git a/testdata/memcached-molecule-operator/roles/foo/vars/main.yml b/testdata/memcached-molecule-operator/roles/foo/vars/main.yml new file mode 100644 index 0000000..5821d4a --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/foo/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for Foo diff --git a/testdata/memcached-molecule-operator/roles/memcached/README.md b/testdata/memcached-molecule-operator/roles/memcached/README.md new file mode 100644 index 0000000..c37ca91 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memcached/README.md @@ -0,0 +1,43 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, +if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in +defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables +that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set +for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for +users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/testdata/ansible/memcached-operator/roles/memcached/defaults/main.yml b/testdata/memcached-molecule-operator/roles/memcached/defaults/main.yml similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/defaults/main.yml rename to testdata/memcached-molecule-operator/roles/memcached/defaults/main.yml diff --git a/testdata/memcached-molecule-operator/roles/memcached/files/.placeholder b/testdata/memcached-molecule-operator/roles/memcached/files/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/ansible/memcached-operator/roles/memcached/handlers/main.yml b/testdata/memcached-molecule-operator/roles/memcached/handlers/main.yml similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/handlers/main.yml rename to testdata/memcached-molecule-operator/roles/memcached/handlers/main.yml diff --git a/testdata/memcached-molecule-operator/roles/memcached/meta/main.yml b/testdata/memcached-molecule-operator/roles/memcached/meta/main.yml new file mode 100644 index 0000000..dfab20d --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memcached/meta/main.yml @@ -0,0 +1,64 @@ +--- +galaxy_info: + author: your name + description: your description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Some suggested licenses: + # - BSD (default) + # - MIT + # - GPLv2 + # - GPLv3 + # - Apache + # - CC-BY + license: license (GPLv2, CC-BY, etc) + + min_ansible_version: 2.9 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # Optionally specify the branch Galaxy will use when accessing the GitHub + # repo for this role. During role install, if no tags are available, + # Galaxy will use this branch. During import Galaxy will access files on + # this branch. If Travis integration is configured, only notifications for this + # branch will be accepted. Otherwise, in all cases, the repo's default branch + # (usually master) will be used. + #github_branch: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. +collections: +- operator_sdk.util +- kubernetes.core diff --git a/testdata/ansible/memcached-operator/roles/memcached/tasks/main.yml b/testdata/memcached-molecule-operator/roles/memcached/tasks/main.yml similarity index 79% rename from testdata/ansible/memcached-operator/roles/memcached/tasks/main.yml rename to testdata/memcached-molecule-operator/roles/memcached/tasks/main.yml index 3fad70b..54d7b1c 100644 --- a/testdata/ansible/memcached-operator/roles/memcached/tasks/main.yml +++ b/testdata/memcached-molecule-operator/roles/memcached/tasks/main.yml @@ -178,3 +178,44 @@ observe: 2 when: not metricsbumped.stat.exists +- operator_sdk.util.k8s_status: + api_version: cache.example.com/v1alpha1 + kind: Memcached + name: "{{ ansible_operator_meta.name }}" + namespace: "{{ ansible_operator_meta.namespace }}" + status: + test: "hello world" + +- kubernetes.core.k8s: + definition: + kind: Secret + apiVersion: v1 + metadata: + name: test-secret + namespace: "{{ ansible_operator_meta.namespace }}" + data: + test: aGVsbG8K +- name: Get cluster api_groups + set_fact: + api_groups: "{{ lookup('kubernetes.core.k8s', cluster_info='api_groups', kubeconfig=lookup('env', 'K8S_AUTH_KUBECONFIG')) }}" + +- name: create project if projects are available + kubernetes.core.k8s: + definition: + apiVersion: project.openshift.io/v1 + kind: Project + metadata: + name: testing-foo + when: "'project.openshift.io' in api_groups" + +- name: Create ConfigMap to test blacklisted watches + kubernetes.core.k8s: + definition: + kind: ConfigMap + apiVersion: v1 + metadata: + name: test-blacklist-watches + namespace: "{{ ansible_operator_meta.namespace }}" + data: + arbitrary: afdasdfsajsafj + state: present diff --git a/testdata/memcached-molecule-operator/roles/memcached/templates/.placeholder b/testdata/memcached-molecule-operator/roles/memcached/templates/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/ansible/memcached-operator/roles/memcached/vars/main.yml b/testdata/memcached-molecule-operator/roles/memcached/vars/main.yml similarity index 100% rename from testdata/ansible/memcached-operator/roles/memcached/vars/main.yml rename to testdata/memcached-molecule-operator/roles/memcached/vars/main.yml diff --git a/testdata/memcached-molecule-operator/roles/memfin/README.md b/testdata/memcached-molecule-operator/roles/memfin/README.md new file mode 100644 index 0000000..c37ca91 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/README.md @@ -0,0 +1,43 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, +if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in +defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables +that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set +for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for +users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/testdata/memcached-molecule-operator/roles/memfin/defaults/main.yml b/testdata/memcached-molecule-operator/roles/memfin/defaults/main.yml new file mode 100644 index 0000000..93d7ae0 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for Memfin diff --git a/testdata/memcached-molecule-operator/roles/memfin/files/.placeholder b/testdata/memcached-molecule-operator/roles/memfin/files/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/memcached-molecule-operator/roles/memfin/handlers/main.yml b/testdata/memcached-molecule-operator/roles/memfin/handlers/main.yml new file mode 100644 index 0000000..6254f28 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for Memfin diff --git a/testdata/memcached-molecule-operator/roles/memfin/meta/main.yml b/testdata/memcached-molecule-operator/roles/memfin/meta/main.yml new file mode 100644 index 0000000..dfab20d --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/meta/main.yml @@ -0,0 +1,64 @@ +--- +galaxy_info: + author: your name + description: your description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Some suggested licenses: + # - BSD (default) + # - MIT + # - GPLv2 + # - GPLv3 + # - Apache + # - CC-BY + license: license (GPLv2, CC-BY, etc) + + min_ansible_version: 2.9 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # Optionally specify the branch Galaxy will use when accessing the GitHub + # repo for this role. During role install, if no tags are available, + # Galaxy will use this branch. During import Galaxy will access files on + # this branch. If Travis integration is configured, only notifications for this + # branch will be accepted. Otherwise, in all cases, the repo's default branch + # (usually master) will be used. + #github_branch: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. +collections: +- operator_sdk.util +- kubernetes.core diff --git a/testdata/memcached-molecule-operator/roles/memfin/tasks/main.yml b/testdata/memcached-molecule-operator/roles/memfin/tasks/main.yml new file mode 100644 index 0000000..e24c947 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- name: delete configmap for test + kubernetes.core.k8s: + kind: ConfigMap + api_version: v1 + name: deleteme + namespace: default + state: absent diff --git a/testdata/memcached-molecule-operator/roles/memfin/templates/.placeholder b/testdata/memcached-molecule-operator/roles/memfin/templates/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/memcached-molecule-operator/roles/memfin/vars/main.yml b/testdata/memcached-molecule-operator/roles/memfin/vars/main.yml new file mode 100644 index 0000000..cce9e51 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/memfin/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for Memfin diff --git a/testdata/memcached-molecule-operator/roles/secret/README.md b/testdata/memcached-molecule-operator/roles/secret/README.md new file mode 100644 index 0000000..c37ca91 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/README.md @@ -0,0 +1,43 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, +if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in +defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables +that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set +for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for +users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/testdata/memcached-molecule-operator/roles/secret/defaults/main.yml b/testdata/memcached-molecule-operator/roles/secret/defaults/main.yml new file mode 100644 index 0000000..3cfaa2a --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for Secret diff --git a/testdata/memcached-molecule-operator/roles/secret/files/.placeholder b/testdata/memcached-molecule-operator/roles/secret/files/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/memcached-molecule-operator/roles/secret/handlers/main.yml b/testdata/memcached-molecule-operator/roles/secret/handlers/main.yml new file mode 100644 index 0000000..ab3ac98 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for Secret diff --git a/testdata/memcached-molecule-operator/roles/secret/meta/main.yml b/testdata/memcached-molecule-operator/roles/secret/meta/main.yml new file mode 100644 index 0000000..dfab20d --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/meta/main.yml @@ -0,0 +1,64 @@ +--- +galaxy_info: + author: your name + description: your description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Some suggested licenses: + # - BSD (default) + # - MIT + # - GPLv2 + # - GPLv3 + # - Apache + # - CC-BY + license: license (GPLv2, CC-BY, etc) + + min_ansible_version: 2.9 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # Optionally specify the branch Galaxy will use when accessing the GitHub + # repo for this role. During role install, if no tags are available, + # Galaxy will use this branch. During import Galaxy will access files on + # this branch. If Travis integration is configured, only notifications for this + # branch will be accepted. Otherwise, in all cases, the repo's default branch + # (usually master) will be used. + #github_branch: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. +collections: +- operator_sdk.util +- kubernetes.core diff --git a/testdata/memcached-molecule-operator/roles/secret/tasks/main.yml b/testdata/memcached-molecule-operator/roles/secret/tasks/main.yml new file mode 100644 index 0000000..1c1bacf --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/tasks/main.yml @@ -0,0 +1,15 @@ +- name: Create test service + kubernetes.core.k8s: + definition: + kind: Service + api_version: v1 + metadata: + name: test-service + namespace: default + spec: + ports: + - protocol: TCP + port: 8332 + targetPort: 8332 + name: rpc + diff --git a/testdata/memcached-molecule-operator/roles/secret/templates/.placeholder b/testdata/memcached-molecule-operator/roles/secret/templates/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/memcached-molecule-operator/roles/secret/vars/main.yml b/testdata/memcached-molecule-operator/roles/secret/vars/main.yml new file mode 100644 index 0000000..1d64ba2 --- /dev/null +++ b/testdata/memcached-molecule-operator/roles/secret/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for Secret diff --git a/testdata/memcached-molecule-operator/watches.yaml b/testdata/memcached-molecule-operator/watches.yaml new file mode 100644 index 0000000..1bc2591 --- /dev/null +++ b/testdata/memcached-molecule-operator/watches.yaml @@ -0,0 +1,27 @@ +--- +# Use the 'create api' subcommand to add watches to this file. +- version: v1alpha1 + group: cache.example.com + kind: Memcached + playbook: playbooks/memcached.yml + finalizer: + name: cache.example.com/finalizer + role: memfin + blacklist: + - group: "" + version: v1 + kind: ConfigMap +- version: v1alpha1 + group: cache.example.com + kind: Foo + playbook: playbooks/foo.yml +- version: v1alpha1 + group: cache.example.com + kind: Memfin + playbook: playbooks/memfin.yml +- version: v1 + group: "" + kind: Secret + role: secret + manageStatus: false +#+kubebuilder:scaffold:watch From d1466de06d7f31f0a446740546f1e0cbf919f8fa Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Mon, 14 Aug 2023 13:17:49 -0400 Subject: [PATCH 2/3] update sanity test failures Signed-off-by: Bryce Palmer --- .../samples/ansible/memcached_molecule.go | 4 +- pkg/testutils/command/command.go | 5 +- pkg/testutils/e2e/helpers.go | 5 +- pkg/testutils/e2e/olm/helpers.go | 10 +- pkg/testutils/e2e/operator/helpers.go | 2 +- pkg/testutils/kubernetes/version.go | 4 +- pkg/testutils/sample/cli/cli.go | 6 +- test/e2e/ansible/suite_test.go | 145 +----------------- 8 files changed, 20 insertions(+), 161 deletions(-) diff --git a/hack/generate/samples/ansible/memcached_molecule.go b/hack/generate/samples/ansible/memcached_molecule.go index e3936d4..66fd97d 100644 --- a/hack/generate/samples/ansible/memcached_molecule.go +++ b/hack/generate/samples/ansible/memcached_molecule.go @@ -16,7 +16,7 @@ package ansible import ( "fmt" - "io/ioutil" + "os" "os/exec" "path/filepath" "strings" @@ -118,7 +118,7 @@ func ImplementMemcachedMolecule(sample sample.Sample, image string) { // prevent high load of controller caused by watching all the secrets in the cluster watchNamespacePatchFileName := "watch_namespace_patch.yaml" log.Info("adding WATCH_NAMESPACE env patch to watch own namespace") - err = ioutil.WriteFile(filepath.Join(sample.Dir(), "config", "testing", watchNamespacePatchFileName), []byte(watchNamespacePatch), 0644) + err = os.WriteFile(filepath.Join(sample.Dir(), "config", "testing", watchNamespacePatchFileName), []byte(watchNamespacePatch), 0644) pkg.CheckError("adding watch_namespace_patch.yaml", err) log.Info("adding WATCH_NAMESPACE env patch to patch list to be applied") diff --git a/pkg/testutils/command/command.go b/pkg/testutils/command/command.go index f108dd5..e1821c0 100644 --- a/pkg/testutils/command/command.go +++ b/pkg/testutils/command/command.go @@ -74,7 +74,10 @@ func (gcc *GenericCommandContext) Run(cmd *exec.Cmd, path ...string) ([]byte, er // make the directory if it does not already exist if dir != "" { if _, err := os.Stat(dir); os.IsNotExist(err) { - os.MkdirAll(dir, 0755) + err = os.MkdirAll(dir, 0755) + if err != nil { + return nil, fmt.Errorf("making all directories: %w", err) + } } } diff --git a/pkg/testutils/e2e/helpers.go b/pkg/testutils/e2e/helpers.go index cbf38c7..5d2f13b 100644 --- a/pkg/testutils/e2e/helpers.go +++ b/pkg/testutils/e2e/helpers.go @@ -2,7 +2,6 @@ package e2e import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -38,13 +37,13 @@ func CreateCustomResources(sample sample.Sample, kubectl kubernetes.Kubectl) err func AllowProjectBeMultiGroup(sample sample.Sample) error { const multiGroup = `multigroup: true ` - projectBytes, err := ioutil.ReadFile(filepath.Join(sample.Dir(), "PROJECT")) + projectBytes, err := os.ReadFile(filepath.Join(sample.Dir(), "PROJECT")) if err != nil { return err } projectBytes = append([]byte(multiGroup), projectBytes...) - err = ioutil.WriteFile(filepath.Join(sample.Dir(), "PROJECT"), projectBytes, 0644) + err = os.WriteFile(filepath.Join(sample.Dir(), "PROJECT"), projectBytes, 0644) if err != nil { return err } diff --git a/pkg/testutils/e2e/olm/helpers.go b/pkg/testutils/e2e/olm/helpers.go index 4713184..01a1d5b 100644 --- a/pkg/testutils/e2e/olm/helpers.go +++ b/pkg/testutils/e2e/olm/helpers.go @@ -2,7 +2,7 @@ package olm import ( "fmt" - "io/ioutil" + "os" "os/exec" "path/filepath" "regexp" @@ -40,7 +40,7 @@ packagemanifests: kustomize %s $(KUSTOMIZE) build config/manifests | operator-sdk generate packagemanifests -q --version $(VERSION) $(PKG_MAN_OPTS) ` - makefileBytes, err := ioutil.ReadFile(filepath.Join(sample.Dir(), "Makefile")) + makefileBytes, err := os.ReadFile(filepath.Join(sample.Dir(), "Makefile")) if err != nil { return err } @@ -54,7 +54,7 @@ packagemanifests: kustomize %s // update makefile by adding the packagemanifests target makefileBytes = append([]byte(makefilePackagemanifestsFragment), makefileBytes...) - err = ioutil.WriteFile(filepath.Join(sample.Dir(), "Makefile"), makefileBytes, 0644) + err = os.WriteFile(filepath.Join(sample.Dir(), "Makefile"), makefileBytes, 0644) if err != nil { return err } @@ -140,14 +140,14 @@ func removeAllAnnotationLines(annotations map[string]string, filePaths []string) } for _, file := range filePaths { - b, err := ioutil.ReadFile(file) + b, err := os.ReadFile(file) if err != nil { return err } for _, re := range annotationREs { b = re.ReplaceAll(b, []byte{}) } - err = ioutil.WriteFile(file, b, 0644) + err = os.WriteFile(file, b, 0644) if err != nil { return err } diff --git a/pkg/testutils/e2e/operator/helpers.go b/pkg/testutils/e2e/operator/helpers.go index 0e3c4a0..8a58559 100644 --- a/pkg/testutils/e2e/operator/helpers.go +++ b/pkg/testutils/e2e/operator/helpers.go @@ -37,7 +37,7 @@ func UndeployOperator(sample sample.Sample) error { cmd := exec.Command("make", "undeploy") _, err := sample.CommandContext().Run(cmd, sample.Name()) if err != nil { - fmt.Errorf("encountered an error when undeploying the operator: %w", err) + return fmt.Errorf("encountered an error when undeploying the operator: %w", err) } return nil diff --git a/pkg/testutils/kubernetes/version.go b/pkg/testutils/kubernetes/version.go index bc0457b..ffe455a 100644 --- a/pkg/testutils/kubernetes/version.go +++ b/pkg/testutils/kubernetes/version.go @@ -63,8 +63,8 @@ func (kvi *KubeVersionInfo) GitVersion() string { // KubeVersion is an implementation of the KubernetesVersion interface type KubeVersion struct { - clientVersion KubeVersionInfo `json:"clientVersion,omitempty"` - serverVersion KubeVersionInfo `json:"serverVersion,omitempty"` + clientVersion KubeVersionInfo + serverVersion KubeVersionInfo } // KubeVersionOptions is for configuring a KubeVersion diff --git a/pkg/testutils/sample/cli/cli.go b/pkg/testutils/sample/cli/cli.go index bddf147..04a53a8 100644 --- a/pkg/testutils/sample/cli/cli.go +++ b/pkg/testutils/sample/cli/cli.go @@ -192,7 +192,7 @@ func (gs *CliSample) GenerateInit() error { return fmt.Errorf("encountered an error getting the current working directory: %w", err) } // defer returning to the original working directory - defer func() { os.Chdir(cwd) }() + defer func() { _ = os.Chdir(cwd) }() // Change directory to the context specified err = os.MkdirAll(gs.Dir(), 0777) if err != nil { @@ -239,7 +239,7 @@ func (gs *CliSample) GenerateApi() error { return fmt.Errorf("encountered an error getting the current working directory: %w", err) } // defer returning to the original working directory - defer func() { os.Chdir(cwd) }() + defer func() { _ = os.Chdir(cwd) }() // Change directory to the context specified err = os.MkdirAll(gs.Dir(), os.ModeDir) if err != nil { @@ -288,7 +288,7 @@ func (gs *CliSample) GenerateWebhook() error { return fmt.Errorf("encountered an error getting the current working directory: %w", err) } // defer returning to the original working directory - defer func() { os.Chdir(cwd) }() + defer func() { _ = os.Chdir(cwd) }() err = os.MkdirAll(gs.Dir(), os.ModeDir) if err != nil { return fmt.Errorf("encountered an error creating context directory: %w", err) diff --git a/test/e2e/ansible/suite_test.go b/test/e2e/ansible/suite_test.go index 0c671a7..47de4a7 100644 --- a/test/e2e/ansible/suite_test.go +++ b/test/e2e/ansible/suite_test.go @@ -106,69 +106,6 @@ var _ = BeforeSuite(func() { }, 3*time.Minute, time.Second).Should(Succeed()) } - // By("replacing project Dockerfile to use ansible base image with the dev tag") - // err = kbutil.ReplaceRegexInFile(filepath.Join(ansibleSample.Dir(), "Dockerfile"), "quay.io/operator-framework/ansible-operator:.*", "quay.io/operator-framework/ansible-operator:dev") - // Expect(err).Should(Succeed()) - - // TODO(everettraven): IMO this should be moved to the logic for implementing the sample. Keeping as is for now to make finish the PoC easier - // --------------------------------------------------- - - // gvk := ansibleSample.GVKs()[0] - - // By("adding Memcached mock task to the role") - // err = kbutil.InsertCode(filepath.Join(ansibleSample.Dir(), "roles", strings.ToLower(gvk.Kind), "tasks", "main.yml"), - // "periodSeconds: 3", memcachedWithBlackListTask) - // Expect(err).NotTo(HaveOccurred()) - - // By("creating an API definition to add a task to delete the config map") - // cmd := exec.Command(ansibleSample.Binary(), "create", "api", - // "--group", gvk.Group, - // "--version", gvk.Version, - // "--kind", "Memfin", - // "--generate-role") - // _, err = ansibleSample.CommandContext().Run(cmd, ansibleSample.Name()) - // Expect(err).NotTo(HaveOccurred()) - - // By("updating spec of Memfin sample") - // err = kbutil.ReplaceInFile( - // filepath.Join(ansibleSample.Dir(), "config", "samples", fmt.Sprintf("%s_%s_memfin.yaml", gvk.Group, gvk.Version)), - // "# TODO(user): Add fields here", - // "foo: bar") - // Expect(err).NotTo(HaveOccurred()) - - // By("adding task to delete config map") - // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "roles", "memfin", "tasks", "main.yml"), - // "# tasks file for Memfin", taskToDeleteConfigMap) - // Expect(err).NotTo(HaveOccurred()) - - // By("adding to watches finalizer and blacklist") - // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "watches.yaml"), - // "playbook: playbooks/memcached.yml", memcachedWatchCustomizations) - // Expect(err).NotTo(HaveOccurred()) - - // By("create API to test watching multiple GVKs") - // cmd = exec.Command(ansibleSample.Binary(), "create", "api", - // "--group", gvk.Group, - // "--version", gvk.Version, - // "--kind", "Foo", - // "--generate-role") - // _, err = ansibleSample.CommandContext().Run(cmd, ansibleSample.Name()) - // Expect(err).NotTo(HaveOccurred()) - - // By("updating spec of Foo sample") - // err = kbutil.ReplaceInFile( - // filepath.Join(ansibleSample.Dir(), "config", "samples", fmt.Sprintf("%s_%s_foo.yaml", gvk.Group, gvk.Version)), - // "# TODO(user): Add fields here", - // "foo: bar") - // Expect(err).NotTo(HaveOccurred()) - - // By("adding RBAC permissions for the Memcached Kind") - // err = kbutil.ReplaceInFile(filepath.Join(ansibleSample.Dir(), "config", "rbac", "role.yaml"), - // "#+kubebuilder:scaffold:rules", rolesForBaseOperator) - // Expect(err).NotTo(HaveOccurred()) - - // --------------------------------------------------- - By("building the project image") err = operator.BuildOperatorImage(ansibleSample, image) Expect(err).NotTo(HaveOccurred()) @@ -188,7 +125,7 @@ var _ = AfterSuite(func() { By("uninstalling prerequisites") if isPrometheusManagedBySuite { By("uninstalling Prometheus") - prometheus.UninstallPrometheusOperator(kctl) + Expect(prometheus.UninstallPrometheusOperator(kctl)).To(Succeed()) } By("destroying container image and work dir") @@ -200,83 +137,3 @@ var _ = AfterSuite(func() { Expect(err).To(BeNil()) } }) - -const memcachedWithBlackListTask = ` - -- operator_sdk.util.k8s_status: - api_version: cache.example.com/v1alpha1 - kind: Memcached - name: "{{ ansible_operator_meta.name }}" - namespace: "{{ ansible_operator_meta.namespace }}" - status: - test: "hello world" - -- kubernetes.core.k8s: - definition: - kind: Secret - apiVersion: v1 - metadata: - name: test-secret - namespace: "{{ ansible_operator_meta.namespace }}" - data: - test: aGVsbG8K -- name: Get cluster api_groups - set_fact: - api_groups: "{{ lookup('kubernetes.core.k8s', cluster_info='api_groups', kubeconfig=lookup('env', 'K8S_AUTH_KUBECONFIG')) }}" - -- name: create project if projects are available - kubernetes.core.k8s: - definition: - apiVersion: project.openshift.io/v1 - kind: Project - metadata: - name: testing-foo - when: "'project.openshift.io' in api_groups" - -- name: Create ConfigMap to test blacklisted watches - kubernetes.core.k8s: - definition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: test-blacklist-watches - namespace: "{{ ansible_operator_meta.namespace }}" - data: - arbitrary: afdasdfsajsafj - state: present` - -const taskToDeleteConfigMap = `- name: delete configmap for test - kubernetes.core.k8s: - kind: ConfigMap - api_version: v1 - name: deleteme - namespace: default - state: absent` - -const memcachedWatchCustomizations = `playbook: playbooks/memcached.yml - finalizer: - name: cache.example.com/finalizer - role: memfin - blacklist: - - group: "" - version: v1 - kind: ConfigMap` - -const rolesForBaseOperator = ` - ## - ## Apply customize roles for base operator - ## - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -#+kubebuilder:scaffold:rules -` From fc36993d77da56937ec38c19d6d7923c330c5a7f Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Wed, 16 Aug 2023 14:37:04 -0400 Subject: [PATCH 3/3] some minor cleanup Signed-off-by: Bryce Palmer --- pkg/testutils/e2e/certmanager/helpers.go | 14 +- pkg/testutils/e2e/olm/helpers.go | 173 ---------------- pkg/testutils/sample/cli/cli.go | 41 +--- pkg/testutils/sample/sample.go | 239 ----------------------- 4 files changed, 5 insertions(+), 462 deletions(-) delete mode 100644 pkg/testutils/e2e/olm/helpers.go diff --git a/pkg/testutils/e2e/certmanager/helpers.go b/pkg/testutils/e2e/certmanager/helpers.go index 06dc432..c6d1de9 100644 --- a/pkg/testutils/e2e/certmanager/helpers.go +++ b/pkg/testutils/e2e/certmanager/helpers.go @@ -8,8 +8,8 @@ import ( ) // InstallCertManagerBundle installs CertManager onto a Kubernetes cluster -func InstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) error { - url, err := getCertManagerURL(hasv1beta1CRs, kubectl) +func InstallCertManagerBundle(kubectl kubernetes.Kubectl) error { + url, err := getCertManagerURL(kubectl) if err != nil { return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) } @@ -34,7 +34,7 @@ func InstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) er // UninstallCertManagerBundle uninstalls CertManager from a Kubernetes cluster func UninstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) error { - url, err := getCertManagerURL(hasv1beta1CRs, kubectl) + url, err := getCertManagerURL(kubectl) if err != nil { return fmt.Errorf("encountered an error when getting the bundle URL: %w", err) } @@ -49,14 +49,12 @@ func UninstallCertManagerBundle(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) // getCertManagerUrl is a helper function to determine the CertManager // that should be installed on a cluster based on the Kubernetes version -func getCertManagerURL(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) (string, error) { - certmanagerVersionWithv1beta2CRs := "v0.11.0" +func getCertManagerURL(kubectl kubernetes.Kubectl) (string, error) { certmanagerLegacyVersion := "v1.0.4" certmanagerVersion := "v1.5.3" certmanagerURLTmplLegacy := "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager-legacy.yaml" certmanagerURLTmpl := "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" - // Return a URL for the manifest bundle with v1beta1 CRs. kubeVersion, err := kubectl.Version() if err != nil { @@ -72,10 +70,6 @@ func getCertManagerURL(hasv1beta1CRs bool, kubectl kubernetes.Kubectl) (string, return "", fmt.Errorf("encountered an error trying to parse Kubernetes Minor Version: %w", err) } - if hasv1beta1CRs { - return fmt.Sprintf(certmanagerURLTmpl, certmanagerVersionWithv1beta2CRs), nil - } - // Determine which URL to use for a manifest bundle with v1 CRs. // The most up-to-date bundle uses v1 CRDs, which were introduced in k8s v1.16. if serverMajor <= 1 && serverMinor < 16 { diff --git a/pkg/testutils/e2e/olm/helpers.go b/pkg/testutils/e2e/olm/helpers.go deleted file mode 100644 index 01a1d5b..0000000 --- a/pkg/testutils/e2e/olm/helpers.go +++ /dev/null @@ -1,173 +0,0 @@ -package olm - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - - "github.com/operator-framework/ansible-operator-plugins/internal/annotations/metrics" - "github.com/operator-framework/ansible-operator-plugins/internal/util/projutil" - "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/sample" - kbutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" -) - -const ( - OlmVersionForTestSuite = "0.20.0" -) - -// AddPackagemanifestsTarget will append the packagemanifests target to the makefile -// in order to test the steps described in the docs. -// More info: https://v1-0-x.sdk.operatorframework.io/docs/olm-integration/generation/#package-manifests-formats -func AddPackagemanifestsTarget(sample sample.Sample, operatorType projutil.OperatorType) error { - var makefilePackagemanifestsFragment = `# Options for "packagemanifests". -ifneq ($(origin FROM_VERSION), undefined) -PKG_FROM_VERSION := --from-version=$(FROM_VERSION) -endif -ifneq ($(origin CHANNEL), undefined) -PKG_CHANNELS := --channel=$(CHANNEL) -endif -ifeq ($(IS_CHANNEL_DEFAULT), 1) -PKG_IS_DEFAULT_CHANNEL := --default-channel -endif -PKG_MAN_OPTS ?= $(PKG_FROM_VERSION) $(PKG_CHANNELS) $(PKG_IS_DEFAULT_CHANNEL) - -# Generate package manifests. -packagemanifests: kustomize %s - operator-sdk generate kustomize manifests -q --interactive=false - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) - $(KUSTOMIZE) build config/manifests | operator-sdk generate packagemanifests -q --version $(VERSION) $(PKG_MAN_OPTS) -` - - makefileBytes, err := os.ReadFile(filepath.Join(sample.Dir(), "Makefile")) - if err != nil { - return err - } - - // add the manifests target when is a Go project. - replaceTarget := "" - if operatorType == projutil.OperatorTypeGo { - replaceTarget = "manifests" - } - makefilePackagemanifestsFragment = fmt.Sprintf(makefilePackagemanifestsFragment, replaceTarget) - - // update makefile by adding the packagemanifests target - makefileBytes = append([]byte(makefilePackagemanifestsFragment), makefileBytes...) - err = os.WriteFile(filepath.Join(sample.Dir(), "Makefile"), makefileBytes, 0644) - if err != nil { - return err - } - return nil -} - -// DisableOLMBundleInterativeMode will update the Makefile to disable the interactive mode -func DisableManifestsInteractiveMode(sample sample.Sample) error { - // Todo: check if we cannot improve it since the replace/content will exists in the - // pkgmanifest target if it be scaffolded before this call - content := "$(OPERATOR_SDK) generate kustomize manifests" - replace := content + " --interactive=false" - return kbutil.ReplaceInFile(filepath.Join(sample.Dir(), "Makefile"), content, replace) -} - -// GenerateBundle runs all commands to create an operator bundle. -func GenerateBundle(sample sample.Sample, image string) error { - if err := DisableManifestsInteractiveMode(sample); err != nil { - return err - } - - cmd := exec.Command("make", "bundle", "IMG="+image) - if _, err := sample.CommandContext().Run(cmd, sample.Name()); err != nil { - return err - } - - return nil -} - -// InstallOLM runs 'operator-sdk olm install' for specific version -// and returns any errors emitted by that command. -func InstallOLMVersion(sample sample.Sample, version string) error { - cmd := exec.Command(sample.Binary(), "olm", "install", "--version", version, "--timeout", "4m") - _, err := sample.CommandContext().Run(cmd) - return err -} - -// InstallOLM runs 'operator-sdk olm uninstall' and logs any errors emitted by that command. -func UninstallOLM(sample sample.Sample) { - cmd := exec.Command(sample.Binary(), "olm", "uninstall") - if _, err := sample.CommandContext().Run(cmd); err != nil { - fmt.Println("warning: error when uninstalling OLM:", err) - } -} - -// StripBundleAnnotations removes all annotations applied to bundle manifests and metadata -// by operator-sdk/internal/annotations/metrics annotators. Doing so decouples samples -// from which operator-sdk version they were build with, as this information is already -// available in git history. -func StripBundleAnnotations(sample sample.Sample) (err error) { - // Remove metadata labels. - metadataAnnotations := metrics.MakeBundleMetadataLabels("") - metadataFiles := []string{ - filepath.Join(sample.Dir(), "bundle", "metadata", "annotations.yaml"), - filepath.Join(sample.Dir(), "bundle.Dockerfile"), - } - if err = removeAllAnnotationLines(metadataAnnotations, metadataFiles); err != nil { - return err - } - - // Remove manifests annotations. - manifestsAnnotations := metrics.MakeBundleObjectAnnotations("") - manifestsFiles := []string{ - filepath.Join(sample.Dir(), "bundle", "manifests", sample.Name()+".clusterserviceversion.yaml"), - filepath.Join(sample.Dir(), "config", "manifests", "bases", sample.Name()+".clusterserviceversion.yaml"), - } - if err = removeAllAnnotationLines(manifestsAnnotations, manifestsFiles); err != nil { - return err - } - - return nil -} - -// removeAllAnnotationLines removes each line containing a key in annotations from all files at filePaths. -func removeAllAnnotationLines(annotations map[string]string, filePaths []string) error { - var annotationREs []*regexp.Regexp - for annotation := range annotations { - re, err := regexp.Compile(".+" + regexp.QuoteMeta(annotation) + ".+\n") - if err != nil { - return fmt.Errorf("compiling annotation regexp: %v", err) - } - annotationREs = append(annotationREs, re) - } - - for _, file := range filePaths { - b, err := os.ReadFile(file) - if err != nil { - return err - } - for _, re := range annotationREs { - b = re.ReplaceAll(b, []byte{}) - } - err = os.WriteFile(file, b, 0644) - if err != nil { - return err - } - } - return nil -} - -// BuildBundleImage is a helper function to run `make bundle-build BUNDLE_IMAGE=` -func BuildBundleImage(sample sample.Sample, bundleImage string) error { - cmd := exec.Command("make", "bundle-build", "BUNDLE_IMG="+bundleImage) - _, err := sample.CommandContext().Run(cmd, sample.Name()) - return err -} - -// GeneratePackageManifests is a helper function to run `make packagemanifests IMG=` -func GeneratePackageManifests(sample sample.Sample, image string) error { - cmd := exec.Command("make", "packagemanifests", "IMG="+image) - o, err := sample.CommandContext().Run(cmd, sample.Name()) - if err != nil { - return fmt.Errorf("encountered an error when generating the packagemanifests: %w | OUTPUT: %s", err, o) - } - return nil -} diff --git a/pkg/testutils/sample/cli/cli.go b/pkg/testutils/sample/cli/cli.go index 04a53a8..38b5e5f 100644 --- a/pkg/testutils/sample/cli/cli.go +++ b/pkg/testutils/sample/cli/cli.go @@ -8,11 +8,6 @@ import ( "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/kubebuilder/v3/pkg/cli" - cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" - "sigs.k8s.io/kubebuilder/v3/pkg/plugin" - kustomizev2Alpha "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v2-alpha" - "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang" - golangv4 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v4" ) // CliSample is a generalized object that implements the Sample interface. It is meant @@ -112,41 +107,7 @@ func WithCLI(c *cli.CLI) CliSampleOption { // NewCliSample will return a new CliSample object. The values used in the CliSample can be modified using CliSampleOption functions func NewCliSample(opts ...CliSampleOption) (*CliSample, error) { - // Create a very basic default CLI with a go/v3 plugin - gov4Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, golangv4.Plugin{}.Version(), - kustomizev2Alpha.Plugin{}, - golangv4.Plugin{}, - ) - - c, err := cli.New( - cli.WithCommandName("cli"), - cli.WithVersion("v0.0.0"), - cli.WithPlugins( - gov4Bundle, - ), - cli.WithDefaultPlugins(cfgv3.Version, gov4Bundle), - cli.WithDefaultProjectVersion(cfgv3.Version), - cli.WithCompletion(), - ) - if err != nil { - return nil, fmt.Errorf("encountered an error creating a new CliSample: %w", err) - } - - gs := &CliSample{ - domain: "example.com", - name: "cli-sample", - gvks: []schema.GroupVersionKind{ - { - Group: "sample", - Version: "v1", - Kind: "Cli", - }, - }, - repo: "", - commandContext: command.NewGenericCommandContext(), - plugins: []string{"go/v3"}, - cli: c, - } + gs := &CliSample{} for _, opt := range opts { opt(gs) diff --git a/pkg/testutils/sample/sample.go b/pkg/testutils/sample/sample.go index cd75780..f0cba58 100644 --- a/pkg/testutils/sample/sample.go +++ b/pkg/testutils/sample/sample.go @@ -1,10 +1,6 @@ package sample import ( - "fmt" - "os/exec" - "strings" - "github.com/operator-framework/ansible-operator-plugins/pkg/testutils/command" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -30,238 +26,3 @@ type Sample interface { // GenerateWebhook scaffolds using the `create webhook` subcommand GenerateWebhook() error } - -// GenericSample is a generalized object that implements the Sample interface. It is meant -// to be as simple and versatile to make the process of generating test samples easier -// TODO: Consider making it easier to create custom cli flags on a per GVK basis for the `create api` and `create webhook` subcommands -type GenericSample struct { - domain string - repo string - gvks []schema.GroupVersionKind - commandContext command.CommandContext - name string - binary string - plugins []string - - initOptions []string - apiOptions []string - webhookOptions []string -} - -// GenericSampleOption is a function that modifies the values in a GenericSample to be used when creating a new GenericSample -type GenericSampleOption func(gs *GenericSample) - -// WithDomain sets the domain to be used during scaffold execution -func WithDomain(domain string) GenericSampleOption { - return func(gs *GenericSample) { - gs.domain = domain - } -} - -// WithRepository sets the repository to be used during scaffold execution -func WithRepository(repo string) GenericSampleOption { - return func(gs *GenericSample) { - gs.repo = repo - } -} - -// WithGvk sets the GroupVersionKind to be used during scaffold execution -func WithGvk(gvks ...schema.GroupVersionKind) GenericSampleOption { - return func(gs *GenericSample) { - gs.gvks = make([]schema.GroupVersionKind, len(gvks)) - copy(gs.gvks, gvks) - } -} - -// WithName sets the name of the sample that is scaffolded -func WithName(name string) GenericSampleOption { - return func(gs *GenericSample) { - gs.name = name - } -} - -// WithCommandContext sets the CommandContext that is used to execute scaffold commands -func WithCommandContext(commandContext command.CommandContext) GenericSampleOption { - return func(gs *GenericSample) { - gs.commandContext = commandContext - } -} - -// WithBinary sets the binary that should be used to run scaffold commands -func WithBinary(binary string) GenericSampleOption { - return func(gs *GenericSample) { - gs.binary = binary - } -} - -// WithPlugins sets the plugins that should be used during scaffolding -func WithPlugins(plugins ...string) GenericSampleOption { - return func(gs *GenericSample) { - gs.plugins = make([]string, len(plugins)) - copy(gs.plugins, plugins) - } -} - -// WithExtraInitOptions sets any additional options that should be passed into an init subcommand -func WithExtraInitOptions(options ...string) GenericSampleOption { - return func(gs *GenericSample) { - gs.initOptions = make([]string, len(options)) - copy(gs.initOptions, options) - } -} - -// WithExtraApiOptions sets any additional options that should be passed into a create api subcommand -func WithExtraApiOptions(options ...string) GenericSampleOption { - return func(gs *GenericSample) { - gs.apiOptions = make([]string, len(options)) - copy(gs.apiOptions, options) - } -} - -// WithExtraWebhookOptions sets any additional options that should be passed into a create webhook subcommand -func WithExtraWebhookOptions(options ...string) GenericSampleOption { - return func(gs *GenericSample) { - gs.webhookOptions = make([]string, len(options)) - copy(gs.webhookOptions, options) - } -} - -// NewGenericSample will return a new GenericSample object. The values used in the GenericSample can be modified using GenericSampleOption functions -func NewGenericSample(opts ...GenericSampleOption) *GenericSample { - gs := &GenericSample{ - domain: "example.com", - name: "generic-sample", - gvks: []schema.GroupVersionKind{ - { - Group: "sample", - Version: "v1", - Kind: "Generic", - }, - }, - // by default use kubebuilder unless otherwise specified - binary: "kubebuilder", - repo: "", - commandContext: command.NewGenericCommandContext(), - plugins: []string{"go/v3"}, - } - - for _, opt := range opts { - opt(gs) - } - - return gs -} - -// CommandContext returns the CommandContext that the GenericSample is using -func (gs *GenericSample) CommandContext() command.CommandContext { - return gs.commandContext -} - -// Name returns the name of the GenericSample -func (gs *GenericSample) Name() string { - return gs.name -} - -// GVKs returns the list of GVKs that is used for creating apis and webhooks -func (gs *GenericSample) GVKs() []schema.GroupVersionKind { - return gs.gvks -} - -// Dir returns the directory the sample is created in -func (gs *GenericSample) Dir() string { - return gs.commandContext.Dir() + "/" + gs.name -} - -// Binary returns the binary used when creating the sample -func (gs *GenericSample) Binary() string { - return gs.binary -} - -// Domain returns the domain of the GenericSample -func (gs *GenericSample) Domain() string { - return gs.domain -} - -// GenerateInit runs the `init` subcommand of the binary provided -func (gs *GenericSample) GenerateInit() error { - options := []string{ - "init", - "--plugins", - strings.TrimRight(strings.Join(gs.plugins, ","), ","), - "--domain", - gs.domain, - } - - if gs.repo != "" { - options = append(options, "--repo", gs.repo) - } - - options = append(options, gs.initOptions...) - - ex := exec.Command(gs.binary, options...) - - output, err := gs.commandContext.Run(ex, gs.name) - if err != nil { - return fmt.Errorf("error running command: %w | output: %s", err, string(output)) - } - - return nil -} - -// GenerateApi runs the `create api` subcommand of the binary provided -func (gs *GenericSample) GenerateApi() error { - for _, gvk := range gs.gvks { - options := []string{ - "create", - "api", - "--plugins", - strings.TrimRight(strings.Join(gs.plugins, ","), ","), - "--group", - gvk.Group, - "--version", - gvk.Version, - "--kind", - gvk.Kind, - } - - options = append(options, gs.apiOptions...) - - ex := exec.Command(gs.binary, options...) - - output, err := gs.commandContext.Run(ex, gs.name) - if err != nil { - return fmt.Errorf("error running command: %w | output: %s", err, string(output)) - } - } - - return nil -} - -// GenerateWebhook runs the `create webhook` subcommand of the binary provided -func (gs *GenericSample) GenerateWebhook() error { - for _, gvk := range gs.gvks { - options := []string{ - "create", - "webhook", - "--plugins", - strings.TrimRight(strings.Join(gs.plugins, ","), ","), - "--group", - gvk.Group, - "--version", - gvk.Version, - "--kind", - gvk.Kind, - } - - options = append(options, gs.webhookOptions...) - - ex := exec.Command(gs.binary, options...) - - output, err := gs.commandContext.Run(ex, gs.name) - if err != nil { - return fmt.Errorf("error running command: %w | output: %s", err, string(output)) - } - } - - return nil -}