From 346e36dafedc4a89daa6113482c515e05cf88135 Mon Sep 17 00:00:00 2001 From: Camila Macedo Date: Sat, 17 Oct 2020 09:08:28 -0300 Subject: [PATCH] feat: replace ansible-memcached by molecule mock with samples --- .gitignore | 5 +- Makefile | 2 +- .../{generate_all.go => generate_testdata.go} | 13 +- .../samples/internal/ansible/constants.go | 418 ++++++++++++++++++ .../samples/internal/ansible/memcached.go | 130 +----- .../samples/internal/ansible/molecule.go | 178 ++++++++ hack/generate/samples/molecule/generate.go | 63 +++ hack/tests/e2e-ansible-molecule.sh | 38 +- internal/testutils/utils.go | 20 + test/ansible-memcached/OWNERS | 6 - test/ansible-memcached/defaults.yml | 1 - test/ansible-memcached/memcached_test.yml | 150 ------- test/ansible-memcached/memfin/tasks/main.yml | 7 - test/ansible-memcached/prepare-test-image.yml | 26 -- test/ansible-memcached/secret/tasks/main.yml | 20 - test/ansible-memcached/tasks.yml | 79 ---- test/ansible-memcached/watches-finalizer.yaml | 7 - test/ansible-memcached/watches-foo-kind.yaml | 4 - test/ansible-memcached/watches-v1-kind.yaml | 5 - .../molecule/default/tasks/memcached_test.yml | 178 +++++--- .../playbooks/memcached.yml | 9 + .../ansible/memcached-operator/watches.yaml | 2 +- 22 files changed, 817 insertions(+), 544 deletions(-) rename hack/generate/samples/{generate_all.go => generate_testdata.go} (85%) create mode 100644 hack/generate/samples/internal/ansible/constants.go create mode 100644 hack/generate/samples/internal/ansible/molecule.go create mode 100644 hack/generate/samples/molecule/generate.go delete mode 100644 test/ansible-memcached/OWNERS delete mode 100644 test/ansible-memcached/defaults.yml delete mode 100644 test/ansible-memcached/memcached_test.yml delete mode 100644 test/ansible-memcached/memfin/tasks/main.yml delete mode 100644 test/ansible-memcached/prepare-test-image.yml delete mode 100644 test/ansible-memcached/secret/tasks/main.yml delete mode 100644 test/ansible-memcached/tasks.yml delete mode 100644 test/ansible-memcached/watches-finalizer.yaml delete mode 100644 test/ansible-memcached/watches-foo-kind.yaml delete mode 100644 test/ansible-memcached/watches-v1-kind.yaml create mode 100644 testdata/ansible/memcached-operator/playbooks/memcached.yml diff --git a/.gitignore b/.gitignore index 72c8c4b841..55838bff56 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,12 @@ website/resources/ website/node_modules/ website/tech-doc-hugo -# samples bin +# Ensure that will not commit the bin gen in the go sample testdata/go/memcached-operator/bin/* +# Ignore molecule samples testdata if it be generated in the testdata/ diretory +testdata/ansible/memcached-molecule-operator + # Trash files *\.DS_Store diff --git a/Makefile b/Makefile index 47f248b9ba..885ed322b9 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ export PATH := $(PWD)/$(BUILD_DIR):$(PWD)/$(TOOLS_DIR):$(PATH) .PHONY: generate generate: build # Generate CLI docs and samples go run ./hack/generate/cli-doc/gen-cli-doc.go - go run ./hack/generate/samples/generate_all.go + go run ./hack/generate/samples/generate_testdata.go .PHONY: bindata OLM_VERSION=0.15.1 diff --git a/hack/generate/samples/generate_all.go b/hack/generate/samples/generate_testdata.go similarity index 85% rename from hack/generate/samples/generate_all.go rename to hack/generate/samples/generate_testdata.go index bb4e89841a..1f51c257ab 100644 --- a/hack/generate/samples/generate_all.go +++ b/hack/generate/samples/generate_testdata.go @@ -27,11 +27,14 @@ import ( "github.com/operator-framework/operator-sdk/internal/testutils" ) -var ( - binaryName string -) - func main() { + // testdata is the path where all samples should be generate + const testdata = "/testdata/" + + // binaryName allow inform the binary that should be used. + // By default it is operator-sdk + var binaryName string + flag.StringVar(&binaryName, "bin", testutils.BinaryName, "Binary path that should be used") flag.Parse() @@ -41,7 +44,7 @@ func main() { os.Exit(1) } - samplesPath := filepath.Join(currentPath, "testdata") + samplesPath := filepath.Join(currentPath, testdata) log.Infof("using the path: (%v)", samplesPath) log.Infof("creating Helm Memcached Sample") diff --git a/hack/generate/samples/internal/ansible/constants.go b/hack/generate/samples/internal/ansible/constants.go new file mode 100644 index 0000000000..04d67a045d --- /dev/null +++ b/hack/generate/samples/internal/ansible/constants.go @@ -0,0 +1,418 @@ +// 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 + +const roleFragment = ` +- name: start memcached + community.kubernetes.k8s: + definition: + kind: Deployment + apiVersion: apps/v1 + metadata: + name: '{{ ansible_operator_meta.name }}-memcached' + namespace: '{{ ansible_operator_meta.namespace }}' + spec: + replicas: "{{size}}" + selector: + matchLabels: + app: memcached + template: + metadata: + labels: + app: memcached + spec: + containers: + - name: memcached + command: + - memcached + - -m=64 + - -o + - modern + - -v + image: "docker.io/memcached:1.4.36-alpine" + ports: + - containerPort: 11211 +` + +const defaultsFragment = `size: 1` + +const moleculeTaskFragment = `- name: Load CR + set_fact: + custom_resource: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" + vars: + cr_file: 'cache_v1alpha1_memcached.yaml' + +- name: Create the cache.example.com/v1alpha1.Memcached + k8s: + state: present + namespace: '{{ namespace }}' + definition: '{{ custom_resource }}' + wait: yes + wait_timeout: 300 + wait_condition: + type: Running + reason: Successful + status: "True" + +- name: Wait 2 minutes for memcached deployment + debug: + var: deploy + until: + - deploy is defined + - deploy.status is defined + - deploy.status.replicas is defined + - deploy.status.replicas == deploy.status.get("availableReplicas", 0) + retries: 12 + delay: 10 + vars: + deploy: '{{ lookup("k8s", + kind="Deployment", + api_version="apps/v1", + namespace=namespace, + label_selector="app=memcached" + )}}' + +- name: Verify custom status exists + assert: + that: debug_cr.status.get("test") == "hello world" + vars: + debug_cr: '{{ lookup("k8s", + kind=custom_resource.kind, + api_version=custom_resource.apiVersion, + namespace=namespace, + resource_name=custom_resource.metadata.name + )}}' + +- when: molecule_yml.scenario.name == "test-local" + block: + - name: Restart the operator by killing the pod + k8s: + state: absent + definition: + api_version: v1 + kind: Pod + metadata: + namespace: '{{ namespace }}' + name: '{{ pod.metadata.name }}' + vars: + pod: '{{ q("k8s", api_version="v1", kind="Pod", namespace=namespace, label_selector="name=%s").0 }}' + + - name: Wait 2 minutes for operator deployment + debug: + var: deploy + until: + - deploy is defined + - deploy.status is defined + - deploy.status.replicas is defined + - deploy.status.replicas == deploy.status.get("availableReplicas", 0) + retries: 12 + delay: 10 + vars: + deploy: '{{ lookup("k8s", + kind="Deployment", + api_version="apps/v1", + namespace=namespace, + resource_name="%s" + )}}' + + - name: Wait for reconciliation to have a chance at finishing + pause: + seconds: 15 + + - name: Delete the service that is created. + k8s: + kind: Service + api_version: v1 + namespace: '{{ namespace }}' + name: test-service + state: absent + + - name: Verify that test-service was re-created + debug: + var: service + until: service + retries: 12 + delay: 10 + vars: + service: '{{ lookup("k8s", + kind="Service", + api_version="v1", + namespace=namespace, + resource_name="test-service", + )}}' + +- name: Delete the custom resource + k8s: + state: absent + namespace: '{{ namespace }}' + definition: '{{ custom_resource }}' + +- name: Wait for the custom resource to be deleted + k8s_info: + api_version: '{{ custom_resource.apiVersion }}' + kind: '{{ custom_resource.kind }}' + namespace: '{{ namespace }}' + name: '{{ custom_resource.metadata.name }}' + register: cr + retries: 10 + delay: 6 + until: not cr.resources + failed_when: cr.resources + +- name: Verify the Deployment was deleted (wait 30s) + assert: + that: not lookup('k8s', kind='Deployment', api_version='apps/v1', namespace=namespace, label_selector='app=memcached') + retries: 10 + delay: 3 +` + +const memcachedCustomStatusMoleculeTarget = `- name: Verify custom status exists + assert: + that: debug_cr.status.get("test") == "hello world" + vars: + debug_cr: '{{ lookup("k8s", + kind=custom_resource.kind, + api_version=custom_resource.apiVersion, + namespace=namespace, + resource_name=custom_resource.metadata.name + )}}'` + +// false positive: G101: Potential hardcoded credentials (gosec) +// nolint:gosec +const testSecretMoleculeCheck = ` + +# 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') +` + +const testFooMoleculeCheck = ` + +- 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')" +` + +// false positive: G101: Potential hardcoded credentials (gosec) +// nolint:gosec +const originalTaskSecret = `--- +# tasks file for Secret +` + +// false positive: G101: Potential hardcoded credentials (gosec) +// nolint:gosec +const taskForSecret = `- name: Create test service + community.kubernetes.k8s: + definition: + kind: Service + api_version: v1 + metadata: + name: test-service + namespace: default + spec: + ports: + - protocol: TCP + port: 8332 + targetPort: 8332 + name: rpc + +- name: Check if jmespath is installed + set_fact: + instance_tags: '{{app | json_query(query)}}' + vars: + query: 'app[*]."memcached"' +` + +// false positive: G101: Potential hardcoded credentials (gosec) +// nolint:gosec +const manageStatusFalseForRoleSecret = `role: secret + manageStatus: false` + +const fixmeAssert = ` +- name: Add assertions here + assert: + that: false + fail_msg: FIXME Add real assertions for your operator +` + +const originaMemcachedMoleculeTask = `- name: Create the cache.example.com/v1alpha1.Memcached + k8s: + state: present + namespace: '{{ namespace }}' + definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" + wait: yes + wait_timeout: 300 + wait_condition: + type: Running + reason: Successful + status: "True" + vars: + cr_file: 'cache_v1alpha1_memcached.yaml' + +- name: Add assertions here + assert: + that: false + fail_msg: FIXME Add real assertions for your operator` + +const targetMoleculeCheckDeployment = `- name: Wait 2 minutes for memcached deployment + debug: + var: deploy + until: + - deploy is defined + - deploy.status is defined + - deploy.status.replicas is defined + - deploy.status.replicas == deploy.status.get("availableReplicas", 0) + retries: 12 + delay: 10 + vars: + deploy: '{{ lookup("k8s", + kind="Deployment", + api_version="apps/v1", + namespace=namespace, + label_selector="app=memcached" + )}}'` + +const molecuTaskToCheckConfigMap = ` + +- name: Create ConfigMap that the Operator should delete + k8s: + definition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: deleteme + namespace: '{{ namespace }}' + data: + delete: me +` + +const memcachedWithBlackListTask = ` +- name: start memcached + community.kubernetes.k8s: + definition: + kind: Deployment + apiVersion: apps/v1 + metadata: + name: '{{ ansible_operator_meta.name }}-memcached' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + app: memcached + spec: + replicas: "{{size}}" + selector: + matchLabels: + app: memcached + template: + metadata: + labels: + app: memcached + spec: + containers: + - name: memcached + command: + - memcached + - -m=64 + - -o + - modern + - -v + image: "docker.io/memcached:1.4.36-alpine" + ports: + - containerPort: 11211 + readinessProbe: + tcpSocket: + port: 11211 + initialDelaySeconds: 3 + periodSeconds: 3 + +- 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" + +- community.kubernetes.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('community.kubernetes.k8s', cluster_info='api_groups', kubeconfig=lookup('env', 'K8S_AUTH_KUBECONFIG')) }}" + +- name: create project if projects are available + community.kubernetes.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 + community.kubernetes.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 + community.kubernetes.k8s: + kind: ConfigMap + api_version: v1 + name: deleteme + namespace: default + state: absent` + +const memcachedWatchCustomizations = `playbook: playbooks/memcached.yml + finalizer: + name: finalizer.cache.example.com + role: memfin + blacklist: + - group: "" + version: v1 + kind: ConfigMap` + +const rolesForBaseOperator = ` + ## + ## Apply customize roles for base operator + ## + - apiGroups: + - "" + resources: + - configmaps + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +# +kubebuilder:scaffold:rules +` diff --git a/hack/generate/samples/internal/ansible/memcached.go b/hack/generate/samples/internal/ansible/memcached.go index b9364e6acb..23347922a2 100644 --- a/hack/generate/samples/internal/ansible/memcached.go +++ b/hack/generate/samples/internal/ansible/memcached.go @@ -63,7 +63,8 @@ func (ma *MemcachedAnsible) Run() { "--version", ma.ctx.Version, "--kind", ma.ctx.Kind, "--domain", ma.ctx.Domain, - "--generate-role") + "--generate-role", + "--generate-playbook") pkg.CheckError("creating the project", err) err = ma.ctx.Make("kustomize") @@ -88,7 +89,7 @@ func (ma *MemcachedAnsible) addingMoleculeMockData() { fmt.Sprintf("%s_test.yml", strings.ToLower(ma.ctx.Kind))) err := testutils.ReplaceInFile(moleculeTaskPath, - moleculeAssertions, moleculeTaskFragment) + originaMemcachedMoleculeTask, fmt.Sprintf(moleculeTaskFragment, ma.ctx.ProjectName, ma.ctx.ProjectName)) pkg.CheckError("replacing molecule default tasks", err) } @@ -126,128 +127,3 @@ func GenerateMemcachedAnsibleSample(samplesPath string) { memcached.Prepare() memcached.Run() } - -const roleFragment = ` -- name: start memcached - community.kubernetes.k8s: - definition: - kind: Deployment - apiVersion: apps/v1 - metadata: - name: '{{ ansible_operator_meta.name }}-memcached' - namespace: '{{ ansible_operator_meta.namespace }}' - spec: - replicas: "{{size}}" - selector: - matchLabels: - app: memcached - template: - metadata: - labels: - app: memcached - spec: - containers: - - name: memcached - command: - - memcached - - -m=64 - - -o - - modern - - -v - image: "docker.io/memcached:1.4.36-alpine" - ports: - - containerPort: 11211 -` - -const defaultsFragment = `size: 1` - -const moleculeAssertions = `- name: Add assertions here - assert: - that: false - fail_msg: FIXME Add real assertions for your operator -` - -const moleculeTaskFragment = `- name: Create the cache.example.com/v1alpha1.Memcached - k8s: - state: present - namespace: "{{ namespace }}" - definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" - wait: yes - wait_timeout: 300 - wait_condition: - type: Running - reason: Successful - status: "True" - vars: - cr_file: 'cache_v1alpha1_memcached.yaml' - -- name: Wait 2 minutes for memcached pod to start - k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list - until: - - pod_list.resources is defined - - pod_list.resources|length == 1 - retries: 12 - delay: 10 - -- name: Delete memcached pod - community.kubernetes.k8s: - state: absent - definition: - kind: Pod - api_version: v1 - metadata: - namespace: "{{ namespace }}" - name: "{{ item.metadata.name }}" - loop: "{{ pod_list.resources }}" - -- name: pause - pause: - seconds: 10 - -- name: Wait 2 minutes for memcached pod to restart - k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list - until: - - pod_list.resources is defined - - pod_list.resources|length == 1 - retries: 12 - delay: 10 - - -- name: Edit Memcached size - k8s: - state: present - namespace: "{{ namespace }}" - definition: - apiVersion: cache.example.com/v1alpha1 - kind: Memcached - metadata: - name: memcached-sample - spec: - size: 3 - -- name: Wait 2 minutes for 3 memcached pods - k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list - until: - - pod_list.resources is defined - - pod_list.resources|length == 1 - retries: 12 - delay: 10 -` diff --git a/hack/generate/samples/internal/ansible/molecule.go b/hack/generate/samples/internal/ansible/molecule.go new file mode 100644 index 0000000000..7c3c74cd83 --- /dev/null +++ b/hack/generate/samples/internal/ansible/molecule.go @@ -0,0 +1,178 @@ +// 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/exec" + "path/filepath" + "strings" + + kbtestutils "sigs.k8s.io/kubebuilder/test/e2e/utils" + + "github.com/operator-framework/operator-sdk/hack/generate/samples/internal/pkg" + "github.com/operator-framework/operator-sdk/internal/testutils" + log "github.com/sirupsen/logrus" +) + +// MoleculeAnsible defines the context for the sample +type MoleculeAnsible struct { + ctx *pkg.SampleContext +} + +// NewMoleculeAnsible return a MoleculeAnsible +func NewMoleculeAnsible(ctx *pkg.SampleContext) MoleculeAnsible { + return MoleculeAnsible{ctx} +} + +// 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 *MoleculeAnsible) 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 *MoleculeAnsible) Run() { + memcached := NewMemcachedAnsible(ma.ctx) + memcached.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 := kbtestutils.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 = kbtestutils.InsertCode(moleculeTaskPath, memcachedCustomStatusMoleculeTarget, testSecretMoleculeCheck) + pkg.CheckError("replacing memcached task to add secret check", err) + + log.Infof("insert molecule task to ensure to foo ") + err = kbtestutils.InsertCode(moleculeTaskPath, testSecretMoleculeCheck, testFooMoleculeCheck) + 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 = testutils.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 = testutils.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 = testutils.ReplaceInFile(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("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("adding task to delete config map") + err = testutils.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 = testutils.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("removing ignore group for the secret from watches as an workaround to work with core types") + err = testutils.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 = testutils.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 = testutils.ReplaceInFile(filepath.Join(ma.ctx.Dir, "watches.yaml"), + "role: secret", manageStatusFalseForRoleSecret) + pkg.CheckError("replacing in watches.yaml", err) + + log.Infof("removing FIXME asserts from memfin_test.yml") + err = testutils.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 = testutils.ReplaceInFile(filepath.Join(ma.ctx.Dir, "molecule", "default", "tasks", "foo_test.yml"), + fixmeAssert, "") + pkg.CheckError("replacing foo_test.yml", err) +} + +// GenerateMoleculeAnsibleSample 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 GenerateMoleculeAnsibleSample(path string) { + ctx, err := pkg.NewSampleContext(testutils.BinaryName, filepath.Join(path, "memcached-molecule-operator"), + "GO111MODULE=on") + pkg.CheckError("generating Ansible Moleule memcached context", err) + + molecule := NewMoleculeAnsible(&ctx) + molecule.Prepare() + molecule.Run() +} diff --git a/hack/generate/samples/molecule/generate.go b/hack/generate/samples/molecule/generate.go new file mode 100644 index 0000000000..5345a2af9b --- /dev/null +++ b/hack/generate/samples/molecule/generate.go @@ -0,0 +1,63 @@ +// 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 main + +import ( + "flag" + "os" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" + + "github.com/operator-framework/operator-sdk/hack/generate/samples/internal/ansible" + "github.com/operator-framework/operator-sdk/internal/testutils" +) + +// This generate is used to run the e2e molecule tests +func main() { + var ( + // binaryName allow inform the binary that should be used. + // By default it is operator-sdk + binaryName string + + // path is the path provided to generate the molecule sample + path string + ) + + // testdata is the path where all samples are generate + const testdata = "/testdata/" + + flag.StringVar(&binaryName, "bin", testutils.BinaryName, "Binary path that should be used") + flag.StringVar(&path, "path", "", "Path where the molecule should be called") + + flag.Parse() + + // If no path be provided then the Molecule sample will be create in the testdata/ansible dir + // It can be helpful to check the mock data used in the e2e molecule tests as to develop this sample + // By default this mock is ignored in the .gitignore + if strings.TrimSpace(path) == "" { + currentPath, err := os.Getwd() + if err != nil { + log.Error(err) + os.Exit(1) + } + path = filepath.Join(currentPath, testdata, "ansible") + } + + log.Infof("creating Ansible Molecule Mock Sample") + log.Infof("using the path: (%v)", path) + ansible.GenerateMoleculeAnsibleSample(path) +} diff --git a/hack/tests/e2e-ansible-molecule.sh b/hack/tests/e2e-ansible-molecule.sh index fbd87c45a0..3cc39c8b93 100755 --- a/hack/tests/e2e-ansible-molecule.sh +++ b/hack/tests/e2e-ansible-molecule.sh @@ -17,35 +17,14 @@ pip3 install --user ansible-lint yamllint pip3 install --user docker==4.2.2 openshift jmespath ansible-galaxy collection install 'community.kubernetes:<1.0.0' -pushd "$TMPDIR" +header_text "Creating molecule sample" +go run ./hack/generate/samples/molecule/generate.go --path=$TMPDIR -header_text "Creating memcached-operator" -mkdir memcached-operator -pushd memcached-operator -operator-sdk init --plugins ansible.sdk.operatorframework.io/v1 \ - --domain example.com \ - --group ansible \ - --version v1alpha1 \ - --kind Memcached \ - --generate-playbook \ - --generate-role -header_text "Replacing operator contents" -cp "$ROOTDIR/test/ansible-memcached/tasks.yml" roles/memcached/tasks/main.yml -cp "$ROOTDIR/test/ansible-memcached/defaults.yml" roles/memcached/defaults/main.yml -cp "$ROOTDIR/test/ansible-memcached/memcached_test.yml" molecule/default/tasks/memcached_test.yml -cp -a "$ROOTDIR/test/ansible-memcached/memfin" roles/ -cp -a "$ROOTDIR/test/ansible-memcached/secret" roles/ -marker=$(tail -n1 watches.yaml) -sed -i'.bak' -e '$ d' watches.yaml;rm -f watches.yaml.bak -cat "$ROOTDIR/test/ansible-memcached/watches-finalizer.yaml" >> watches.yaml -header_text "Append v1 kind to watches to test watching already registered GVK" -cat "$ROOTDIR/test/ansible-memcached/watches-v1-kind.yaml" >> watches.yaml -echo $marker >> watches.yaml -sed -i'.bak' -e '/- secrets/a \ \ \ \ \ \ - services' config/rbac/role.yaml; rm -f config/rbac/role.yaml.bak +pushd "$TMPDIR" +popd +cd $TMPDIR/memcached-molecule-operator -header_text "Test in kind" -sed -i".bak" -E -e 's/(FROM quay.io\/operator-framework\/ansible-operator)(:.*)?/\1:dev/g' Dockerfile; rm -f Dockerfile.bak -OPERATORDIR="$(pwd)" +header_text "Test Kind" make kustomize if [ -f ./bin/kustomize ] ; then KUSTOMIZE="$(realpath ./bin/kustomize)" @@ -54,8 +33,7 @@ else fi KUSTOMIZE_PATH=${KUSTOMIZE} TEST_OPERATOR_NAMESPACE=default molecule test -s kind -popd -popd +cd $TMPDIR KUSTOMIZE_PATH=${KUSTOMIZE} header_text "Test Ansible Molecule scenarios" pushd "${ROOTDIR}/test/ansible" @@ -64,5 +42,3 @@ sed -i".bak" -E -e 's/(FROM quay.io\/operator-framework\/ansible-operator)(:.*)? docker build -f build/Dockerfile -t "$DEST_IMAGE" --no-cache . load_image_if_kind "$DEST_IMAGE" OPERATOR_PULL_POLICY=Never OPERATOR_IMAGE=${DEST_IMAGE} TEST_CLUSTER_PORT=24443 TEST_OPERATOR_NAMESPACE=osdk-test molecule test --all - -popd diff --git a/internal/testutils/utils.go b/internal/testutils/utils.go index 7904cb08ef..52a36726c7 100644 --- a/internal/testutils/utils.go +++ b/internal/testutils/utils.go @@ -236,3 +236,23 @@ func (tc TestContext) UninstallPrerequisites() { tc.UninstallOLM() } } + +// AllowProjectBeMultiGroup will update the PROJECT file with the information to allow we scaffold +// apis with different groups. +// todo(camilamacedo86) : remove this helper when the edit plugin via the bin +// be available. See the Pr: https://github.com/kubernetes-sigs/kubebuilder/pull/1691 +func (tc TestContext) AllowProjectBeMultiGroup() error { + const multiGroup = `multigroup: true +` + projectBytes, err := ioutil.ReadFile(filepath.Join(tc.Dir, "PROJECT")) + if err != nil { + return err + } + + projectBytes = append([]byte(multiGroup), projectBytes...) + err = ioutil.WriteFile(filepath.Join(tc.Dir, "PROJECT"), projectBytes, 0644) + if err != nil { + return err + } + return nil +} diff --git a/test/ansible-memcached/OWNERS b/test/ansible-memcached/OWNERS deleted file mode 100644 index 9702fa3403..0000000000 --- a/test/ansible-memcached/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -approvers: - - fabianvf - - jmrodri -reviewers: - - fabianvf - - jmrodri diff --git a/test/ansible-memcached/defaults.yml b/test/ansible-memcached/defaults.yml deleted file mode 100644 index 34ae51bf00..0000000000 --- a/test/ansible-memcached/defaults.yml +++ /dev/null @@ -1 +0,0 @@ -size: 1 diff --git a/test/ansible-memcached/memcached_test.yml b/test/ansible-memcached/memcached_test.yml deleted file mode 100644 index 6ab38a9988..0000000000 --- a/test/ansible-memcached/memcached_test.yml +++ /dev/null @@ -1,150 +0,0 @@ ---- -- name: Load CR - set_fact: - custom_resource: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" - vars: - cr_file: 'ansible_v1alpha1_memcached.yaml' - -- name: Create the ansible.example.com/v1alpha1.Memcached - k8s: - state: present - namespace: '{{ namespace }}' - definition: '{{ custom_resource }}' - wait: yes - wait_timeout: 300 - wait_condition: - type: Running - reason: Successful - status: "True" - -- name: Wait 2 minutes for memcached deployment - debug: - var: deploy - until: - - deploy is defined - - deploy.status is defined - - deploy.status.replicas is defined - - deploy.status.replicas == deploy.status.get("availableReplicas", 0) - retries: 12 - delay: 10 - vars: - deploy: '{{ lookup("k8s", - kind="Deployment", - api_version="apps/v1", - 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: - that: debug_cr.status.get("test") == "hello world" - vars: - debug_cr: '{{ lookup("k8s", - kind=custom_resource.kind, - api_version=custom_resource.apiVersion, - namespace=namespace, - 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')" - -- when: molecule_yml.scenario.name == "test-local" - block: - - name: Restart the operator by killing the pod - k8s: - state: absent - definition: - api_version: v1 - kind: Pod - metadata: - namespace: '{{ namespace }}' - name: '{{ pod.metadata.name }}' - vars: - pod: '{{ q("k8s", api_version="v1", kind="Pod", namespace=namespace, label_selector="name=memcached-operator").0 }}' - - - name: Wait 2 minutes for operator deployment - debug: - var: deploy - until: - - deploy is defined - - deploy.status is defined - - deploy.status.replicas is defined - - deploy.status.replicas == deploy.status.get("availableReplicas", 0) - retries: 12 - delay: 10 - vars: - deploy: '{{ lookup("k8s", - kind="Deployment", - api_version="apps/v1", - namespace=namespace, - resource_name="memcached-operator" - )}}' - - - name: Wait for reconciliation to have a chance at finishing - pause: - seconds: 15 - - - name: Delete the service that is created. - k8s: - kind: Service - api_version: v1 - namespace: '{{ namespace }}' - name: test-service - state: absent - - - name: Verify that test-service was re-created - debug: - var: service - until: service - retries: 12 - delay: 10 - vars: - service: '{{ lookup("k8s", - kind="Service", - api_version="v1", - namespace=namespace, - resource_name="test-service", - )}}' - -- name: Delete the custom resource - k8s: - state: absent - namespace: '{{ namespace }}' - definition: '{{ custom_resource }}' - -- name: Wait for the custom resource to be deleted - k8s_info: - api_version: '{{ custom_resource.apiVersion }}' - kind: '{{ custom_resource.kind }}' - namespace: '{{ namespace }}' - name: '{{ custom_resource.metadata.name }}' - register: cr - retries: 10 - delay: 6 - until: not cr.resources - failed_when: cr.resources - -- name: Verify the Deployment was deleted (wait 30s) - assert: - that: not lookup('k8s', kind='Deployment', api_version='apps/v1', namespace=namespace, label_selector='app=memcached') - retries: 10 - delay: 3 diff --git a/test/ansible-memcached/memfin/tasks/main.yml b/test/ansible-memcached/memfin/tasks/main.yml deleted file mode 100644 index c16cbd08f8..0000000000 --- a/test/ansible-memcached/memfin/tasks/main.yml +++ /dev/null @@ -1,7 +0,0 @@ -- name: delete configmap for test - community.kubernetes.k8s: - kind: ConfigMap - api_version: v1 - name: deleteme - namespace: default - state: absent diff --git a/test/ansible-memcached/prepare-test-image.yml b/test/ansible-memcached/prepare-test-image.yml deleted file mode 100644 index bfc30da3f6..0000000000 --- a/test/ansible-memcached/prepare-test-image.yml +++ /dev/null @@ -1,26 +0,0 @@ -- name: Dump dev image - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: Dump the dev image - command: docker save -o /tmp/dev-operator.tar quay.io/operator-framework/ansible-operator:dev - - name: Copy the image to the kind container - command: docker cp /tmp/dev-operator.tar kind-test-local:/dev-operator.tar - -- name: Make dev operator image available - hosts: k8s - gather_facts: no - tasks: - - name: Make dev operator available - command: docker load -i /dev-operator.tar - -- name: Clean up - hosts: localhost - connection: local - gather_facts: no - tasks: - - name: remove dev-operator.tar - file: - path: /tmp/dev-operator.tar - state: absent diff --git a/test/ansible-memcached/secret/tasks/main.yml b/test/ansible-memcached/secret/tasks/main.yml deleted file mode 100644 index 6421a02cae..0000000000 --- a/test/ansible-memcached/secret/tasks/main.yml +++ /dev/null @@ -1,20 +0,0 @@ -- name: Create test service - community.kubernetes.k8s: - definition: - kind: Service - api_version: v1 - metadata: - name: test-service - namespace: default - spec: - ports: - - protocol: TCP - port: 8332 - targetPort: 8332 - name: rpc - -- name: Check if jmespath is installed - set_fact: - instance_tags: '{{app | json_query(query)}}' - vars: - query: 'app[*]."memcached"' diff --git a/test/ansible-memcached/tasks.yml b/test/ansible-memcached/tasks.yml deleted file mode 100644 index 86c33c82cf..0000000000 --- a/test/ansible-memcached/tasks.yml +++ /dev/null @@ -1,79 +0,0 @@ ---- -- name: start memcached - community.kubernetes.k8s: - definition: - kind: Deployment - apiVersion: apps/v1 - metadata: - name: '{{ ansible_operator_meta.name }}-memcached' - namespace: '{{ ansible_operator_meta.namespace }}' - labels: - app: memcached - spec: - replicas: "{{size}}" - selector: - matchLabels: - app: memcached - template: - metadata: - labels: - app: memcached - spec: - containers: - - name: memcached - command: - - memcached - - -m=64 - - -o - - modern - - -v - image: "docker.io/memcached:1.4.36-alpine" - ports: - - containerPort: 11211 - readinessProbe: - tcpSocket: - port: 11211 - initialDelaySeconds: 3 - periodSeconds: 3 - -- operator_sdk.util.k8s_status: - api_version: ansible.example.com/v1alpha1 - kind: Memcached - name: "{{ ansible_operator_meta.name }}" - namespace: "{{ ansible_operator_meta.namespace }}" - status: - test: "hello world" - -- community.kubernetes.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('community.kubernetes.k8s', cluster_info='api_groups', kubeconfig=lookup('env', 'K8S_AUTH_KUBECONFIG')) }}" - -- name: create project if projects are available - community.kubernetes.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 - community.kubernetes.k8s: - definition: - kind: ConfigMap - apiVersion: v1 - metadata: - name: test-blacklist-watches - namespace: "{{ ansible_operator_meta.namespace }}" - data: - arbitrary: afdasdfsajsafj - state: present diff --git a/test/ansible-memcached/watches-finalizer.yaml b/test/ansible-memcached/watches-finalizer.yaml deleted file mode 100644 index dda2f1825f..0000000000 --- a/test/ansible-memcached/watches-finalizer.yaml +++ /dev/null @@ -1,7 +0,0 @@ - finalizer: - name: finalizer.ansible.example.com - role: memfin - blacklist: - - group: "" - version: v1 - kind: ConfigMap diff --git a/test/ansible-memcached/watches-foo-kind.yaml b/test/ansible-memcached/watches-foo-kind.yaml deleted file mode 100644 index bcecc7ec62..0000000000 --- a/test/ansible-memcached/watches-foo-kind.yaml +++ /dev/null @@ -1,4 +0,0 @@ -- version: v1alpha1 - group: ansible.example.com - kind: Foo - role: /dev/null diff --git a/test/ansible-memcached/watches-v1-kind.yaml b/test/ansible-memcached/watches-v1-kind.yaml deleted file mode 100644 index 59c428ac23..0000000000 --- a/test/ansible-memcached/watches-v1-kind.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- version: v1 - group: - kind: Secret - role: secret - manageStatus: false diff --git a/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml b/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml index 509d17500d..1fda30ae63 100644 --- a/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml +++ b/testdata/ansible/memcached-operator/molecule/default/tasks/memcached_test.yml @@ -1,98 +1,130 @@ --- -- name: Create the cache.example.com/v1alpha1.Memcached - k8s: - state: present - namespace: '{{ namespace }}' - definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" - wait: yes - wait_timeout: 300 - wait_condition: - type: Running - reason: Successful - status: "True" +- name: Load CR + set_fact: + custom_resource: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" vars: cr_file: 'cache_v1alpha1_memcached.yaml' - name: Create the cache.example.com/v1alpha1.Memcached k8s: state: present - namespace: "{{ namespace }}" - definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" + namespace: '{{ namespace }}' + definition: '{{ custom_resource }}' wait: yes wait_timeout: 300 wait_condition: type: Running reason: Successful status: "True" - vars: - cr_file: 'cache_v1alpha1_memcached.yaml' -- name: Wait 2 minutes for memcached pod to start - k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list +- name: Wait 2 minutes for memcached deployment + debug: + var: deploy until: - - pod_list.resources is defined - - pod_list.resources|length == 1 + - deploy is defined + - deploy.status is defined + - deploy.status.replicas is defined + - deploy.status.replicas == deploy.status.get("availableReplicas", 0) retries: 12 delay: 10 + vars: + deploy: '{{ lookup("k8s", + kind="Deployment", + api_version="apps/v1", + namespace=namespace, + label_selector="app=memcached" + )}}' -- name: Delete memcached pod - community.kubernetes.k8s: - state: absent - definition: - kind: Pod - api_version: v1 - metadata: - namespace: "{{ namespace }}" - name: "{{ item.metadata.name }}" - loop: "{{ pod_list.resources }}" +- name: Verify custom status exists + assert: + that: debug_cr.status.get("test") == "hello world" + vars: + debug_cr: '{{ lookup("k8s", + kind=custom_resource.kind, + api_version=custom_resource.apiVersion, + namespace=namespace, + resource_name=custom_resource.metadata.name + )}}' -- name: pause - pause: - seconds: 10 +- when: molecule_yml.scenario.name == "test-local" + block: + - name: Restart the operator by killing the pod + k8s: + state: absent + definition: + api_version: v1 + kind: Pod + metadata: + namespace: '{{ namespace }}' + name: '{{ pod.metadata.name }}' + vars: + pod: '{{ q("k8s", api_version="v1", kind="Pod", namespace=namespace, label_selector="name=memcached-operator").0 }}' -- name: Wait 2 minutes for memcached pod to restart - k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list - until: - - pod_list.resources is defined - - pod_list.resources|length == 1 - retries: 12 - delay: 10 + - name: Wait 2 minutes for operator deployment + debug: + var: deploy + until: + - deploy is defined + - deploy.status is defined + - deploy.status.replicas is defined + - deploy.status.replicas == deploy.status.get("availableReplicas", 0) + retries: 12 + delay: 10 + vars: + deploy: '{{ lookup("k8s", + kind="Deployment", + api_version="apps/v1", + namespace=namespace, + resource_name="memcached-operator" + )}}' + - name: Wait for reconciliation to have a chance at finishing + pause: + seconds: 15 -- name: Edit Memcached size + - name: Delete the service that is created. + k8s: + kind: Service + api_version: v1 + namespace: '{{ namespace }}' + name: test-service + state: absent + + - name: Verify that test-service was re-created + debug: + var: service + until: service + retries: 12 + delay: 10 + vars: + service: '{{ lookup("k8s", + kind="Service", + api_version="v1", + namespace=namespace, + resource_name="test-service", + )}}' + +- name: Delete the custom resource k8s: - state: present - namespace: "{{ namespace }}" - definition: - apiVersion: cache.example.com/v1alpha1 - kind: Memcached - metadata: - name: memcached-sample - spec: - size: 3 + state: absent + namespace: '{{ namespace }}' + definition: '{{ custom_resource }}' -- name: Wait 2 minutes for 3 memcached pods +- name: Wait for the custom resource to be deleted k8s_info: - kind: "Pod" - api_version: "v1" - namespace: "osdk-test" - label_selectors: - - app = memcached - register: pod_list - until: - - pod_list.resources is defined - - pod_list.resources|length == 1 - retries: 12 - delay: 10 + api_version: '{{ custom_resource.apiVersion }}' + kind: '{{ custom_resource.kind }}' + namespace: '{{ namespace }}' + name: '{{ custom_resource.metadata.name }}' + register: cr + retries: 10 + delay: 6 + until: not cr.resources + failed_when: cr.resources + +- name: Verify the Deployment was deleted (wait 30s) + assert: + that: not lookup('k8s', kind='Deployment', api_version='apps/v1', namespace=namespace, label_selector='app=memcached') + retries: 10 + delay: 3 + diff --git a/testdata/ansible/memcached-operator/playbooks/memcached.yml b/testdata/ansible/memcached-operator/playbooks/memcached.yml new file mode 100644 index 0000000000..d6bce3cc17 --- /dev/null +++ b/testdata/ansible/memcached-operator/playbooks/memcached.yml @@ -0,0 +1,9 @@ +--- +- hosts: localhost + gather_facts: no + collections: + - community.kubernetes + - operator_sdk.util + tasks: + - import_role: + name: "memcached" diff --git a/testdata/ansible/memcached-operator/watches.yaml b/testdata/ansible/memcached-operator/watches.yaml index 9ebc8a9998..ca49be178a 100644 --- a/testdata/ansible/memcached-operator/watches.yaml +++ b/testdata/ansible/memcached-operator/watches.yaml @@ -3,5 +3,5 @@ - version: v1alpha1 group: cache.example.com kind: Memcached - role: memcached + playbook: playbooks/memcached.yml # +kubebuilder:scaffold:watch