diff --git a/Makefile b/Makefile index 2150a8518..91862c39d 100644 --- a/Makefile +++ b/Makefile @@ -124,8 +124,11 @@ E2E_REGISTRY_NAME := docker-registry E2E_REGISTRY_NAMESPACE := operator-controller-e2e export REG_PKG_NAME := registry-operator -export REGISTRY_ROOT := $(E2E_REGISTRY_NAME).$(E2E_REGISTRY_NAMESPACE).svc:5000 -export CATALOG_IMG := $(REGISTRY_ROOT)/e2e/test-catalog:e2e +export LOCAL_REGISTRY_HOST := $(E2E_REGISTRY_NAME).$(E2E_REGISTRY_NAMESPACE).svc:5000 +export CLUSTER_REGISTRY_HOST := localhost:30000 +export E2E_TEST_CATALOG_V1 := e2e/test-catalog:v1 +export E2E_TEST_CATALOG_V2 := e2e/test-catalog:v2 +export CATALOG_IMG := $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V1) .PHONY: test-ext-dev-e2e test-ext-dev-e2e: $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run extension create, upgrade and delete tests. test/extension-developer-e2e/setup.sh $(OPERATOR_SDK) $(CONTAINER_RUNTIME) $(KUSTOMIZE) $(KIND) $(KIND_CLUSTER_NAME) $(E2E_REGISTRY_NAMESPACE) @@ -141,7 +144,8 @@ image-registry: ## Setup in-cluster image registry ./test/tools/image-registry.sh $(E2E_REGISTRY_NAMESPACE) $(E2E_REGISTRY_NAME) build-push-e2e-catalog: ## Build the testdata catalog used for e2e tests and push it to the image registry - ./test/tools/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(CATALOG_IMG) + ./test/tools/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V1) + ./test/tools/build-push-e2e-catalog.sh $(E2E_REGISTRY_NAMESPACE) $(LOCAL_REGISTRY_HOST)/$(E2E_TEST_CATALOG_V2) # When running the e2e suite, you can set the ARTIFACT_PATH variable to the absolute path # of the directory for the operator-controller e2e tests to store the artifacts, which @@ -202,10 +206,10 @@ kind-clean: $(KIND) #EXHELP Delete the kind cluster. $(KIND) delete cluster --name $(KIND_CLUSTER_NAME) registry-load-bundles: ## Load selected e2e testdata container images created in kind-load-bundles into registry - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(REGISTRY_ROOT)/bundles/registry-v1/prometheus-operator:v1.0.0 prometheus-operator.v1.0.0 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(REGISTRY_ROOT)/bundles/registry-v1/prometheus-operator:v1.0.1 prometheus-operator.v1.0.1 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(REGISTRY_ROOT)/bundles/registry-v1/prometheus-operator:v1.2.0 prometheus-operator.v1.2.0 prometheus-operator.v1.0.0 - testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(REGISTRY_ROOT)/bundles/registry-v1/prometheus-operator:v2.0.0 prometheus-operator.v2.0.0 prometheus-operator.v1.0.0 + testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.0.0 prometheus-operator.v1.0.0 prometheus-operator.v1.0.0 + testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.0.1 prometheus-operator.v1.0.1 prometheus-operator.v1.0.0 + testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v1.2.0 prometheus-operator.v1.2.0 prometheus-operator.v1.0.0 + testdata/bundles/registry-v1/build-push-e2e-bundle.sh ${E2E_REGISTRY_NAMESPACE} $(LOCAL_REGISTRY_HOST)/bundles/registry-v1/prometheus-operator:v2.0.0 prometheus-operator.v2.0.0 prometheus-operator.v1.0.0 #SECTION Build diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 3d3162e11..2a9869154 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/google/go-containerregistry/pkg/crane" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" @@ -94,7 +95,7 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { assert.Equal(ct, metav1.ConditionTrue, cond.Status) assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) assert.Contains(ct, cond.Message, "resolved to") - assert.Equal(ct, &ocv1alpha1.BundleMetadata{Name: "prometheus-operator.2.0.0", Version: "2.0.0"}, clusterExtension.Status.ResolvedBundle) + assert.Equal(ct, &ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.2.0", Version: "1.2.0"}, clusterExtension.Status.ResolvedBundle) }, pollDuration, pollInterval) t.Log("By eventually reporting a successful unpacked") @@ -123,75 +124,6 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { }, pollDuration, pollInterval) } -func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { - t.Log("When a cluster extension is installed from a catalog") - t.Log("It resolves again when a new catalog is available") - - clusterExtension, extensionCatalog := testInit(t) - defer testCleanup(t, extensionCatalog, clusterExtension) - defer getArtifactsOutput(t) - - pkgName := "prometheus" - clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - InstallNamespace: "default", - ServiceAccount: ocv1alpha1.ServiceAccountReference{ - Name: "default", - }, - } - - t.Log("By deleting the catalog first") - require.NoError(t, c.Delete(context.Background(), extensionCatalog)) - require.EventuallyWithT(t, func(ct *assert.CollectT) { - err := c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, &catalogd.ClusterCatalog{}) - assert.True(ct, errors.IsNotFound(err)) - }, pollDuration, pollInterval) - - t.Log("By creating the ClusterExtension resource") - require.NoError(t, c.Create(context.Background(), clusterExtension)) - - // TODO: this isn't a good precondition because a missing package results in - // exponential backoff retries. So we can't be sure that the re-reconcile is a result of - // the catalog becoming available because it could also be a retry of the initial failed - // resolution. - t.Log("By failing to find ClusterExtension during resolution") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionFalse, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonResolutionFailed, cond.Reason) - assert.Contains(ct, cond.Message, fmt.Sprintf("no package %q found", pkgName)) - }, pollDuration, pollInterval) - - t.Log("By creating an ClusterExtension catalog with the desired package") - var err error - extensionCatalog, err = createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) - require.NoError(t, err) - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) - cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeUnpacked) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, catalogd.ReasonUnpackSuccessful, cond.Reason) - }, pollDuration, pollInterval) - - t.Log("By eventually resolving the package successfully") - require.EventuallyWithT(t, func(ct *assert.CollectT) { - assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) - cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) - if !assert.NotNil(ct, cond) { - return - } - assert.Equal(ct, metav1.ConditionTrue, cond.Status) - assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) - }, pollDuration, pollInterval) -} - func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("When a cluster extension is installed from a catalog") t.Log("When resolving upgrade edges") @@ -337,6 +269,84 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { }, pollDuration, pollInterval) } +func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { + t.Log("When a cluster extension is installed from a catalog") + t.Log("It resolves again when a new catalog is available") + + // Tag the image with the new tag + var err error + v1Image := fmt.Sprintf("%s/%s", os.Getenv("CLUSTER_REGISTRY_HOST"), os.Getenv("E2E_TEST_CATALOG_V1")) + err = crane.Tag(v1Image, latestImageTag, crane.Insecure) + require.NoError(t, err) + + // create a test-catalog with latest image tag + latestCatalogImage := fmt.Sprintf("%s/e2e/test-catalog:latest", os.Getenv("LOCAL_REGISTRY_HOST")) + extensionCatalog, err := createTestCatalog(context.Background(), testCatalogName, latestCatalogImage) + require.NoError(t, err) + clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8)) + clusterExtension := &ocv1alpha1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterExtensionName, + }, + } + defer testCleanup(t, extensionCatalog, clusterExtension) + defer getArtifactsOutput(t) + + clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ + PackageName: "prometheus", + InstallNamespace: "default", + ServiceAccount: ocv1alpha1.ServiceAccountReference{ + Name: "default", + }, + } + t.Log("It resolves the specified package with correct bundle path") + t.Log("By creating the ClusterExtension resource") + require.NoError(t, c.Create(context.Background(), clusterExtension)) + + t.Log("By reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + assert.Len(ct, clusterExtension.Status.Conditions, len(conditionsets.ConditionTypes)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, &ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.2.0", Version: "1.2.0"}, clusterExtension.Status.ResolvedBundle) + }, pollDuration, pollInterval) + + // update tag on test-catalog image with v2 image + t.Log("By updating the catalog tag to point to the v2 catalog") + v2Image := fmt.Sprintf("%s/%s", os.Getenv("CLUSTER_REGISTRY_HOST"), os.Getenv("E2E_TEST_CATALOG_V2")) + err = crane.Tag(v2Image, latestImageTag, crane.Insecure) + require.NoError(t, err) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.Name}, extensionCatalog)) + cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, catalogd.TypeUnpacked) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, catalogd.ReasonUnpackSuccessful, cond.Reason) + }, pollDuration, pollInterval) + + t.Log("By eventually reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + assert.Len(ct, clusterExtension.Status.Conditions, len(conditionsets.ConditionTypes)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, &ocv1alpha1.BundleMetadata{Name: "prometheus-operator.2.0.0", Version: "2.0.0"}, clusterExtension.Status.ResolvedBundle) + }, pollDuration, pollInterval) +} + // getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path. // Currently it saves: // - clusterextensions diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 8c32390bd..e62da9c5d 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "testing" + "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -24,6 +25,7 @@ var ( const ( testCatalogRefEnvVar = "CATALOG_IMG" testCatalogName = "test-catalog" + latestImageTag = "latest" ) func TestMain(m *testing.M) { @@ -50,6 +52,7 @@ func createTestCatalog(ctx context.Context, name string, imageRef string) (*cata Image: &catalogd.ImageSource{ Ref: imageRef, InsecureSkipTLSVerify: true, + PollInterval: &metav1.Duration{Duration: time.Second}, }, }, }, diff --git a/test/extension-developer-e2e/setup.sh b/test/extension-developer-e2e/setup.sh index ab8e48e79..889080ad6 100755 --- a/test/extension-developer-e2e/setup.sh +++ b/test/extension-developer-e2e/setup.sh @@ -13,7 +13,7 @@ a KinD cluster with the name specified in the arguments. The following environment variables are required for configuring this script: - \$CATALOG_IMG - the tag for the catalog image that contains the registry+v1 bundle. - \$REG_PKG_NAME - the name of the package for the extension that uses the registry+v1 bundle format. -- \$REGISTRY_ROOT - hostname:port of the local docker-registry +- \$LOCAL_REGISTRY_HOST - hostname:port of the local docker-registry setup.sh also takes 5 arguments. Usage: @@ -42,8 +42,8 @@ if [[ -z "${REG_PKG_NAME}" ]]; then exit 1 fi -if [[ -z "${REGISTRY_ROOT}" ]]; then - echo "\$REGISTRY_ROOT is required to be set" +if [[ -z "${LOCAL_REGISTRY_HOST}" ]]; then + echo "\$LOCAL_REGISTRY_HOST is required to be set" echo "${help}" exit 1 fi @@ -69,7 +69,7 @@ kcluster_name=$5 namespace=$6 reg_img="${DOMAIN}/registry:v0.0.1" -reg_bundle_img="${REGISTRY_ROOT}/bundles/registry-v1/registry-bundle:v0.0.1" +reg_bundle_img="${LOCAL_REGISTRY_HOST}/bundles/registry-v1/registry-bundle:v0.0.1" catalog_img="${CATALOG_IMG}" reg_pkg_name="${REG_PKG_NAME}" diff --git a/test/tools/build-push-e2e-catalog.sh b/test/tools/build-push-e2e-catalog.sh index e6d34d86e..9fd1a9d6b 100755 --- a/test/tools/build-push-e2e-catalog.sh +++ b/test/tools/build-push-e2e-catalog.sh @@ -21,45 +21,46 @@ if [[ "$#" -ne 2 ]]; then fi namespace=$1 -tag=$2 +image=$2 +tag=${image##*:} -echo "${namespace}" "${tag}" +echo "${namespace}" "${image}" "${tag}" -kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog.Dockerfile operator-controller-e2e.dockerfile -kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog operator-controller-e2e.build-contents +kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog-${tag}.Dockerfile operator-controller-e2e-${tag}.dockerfile +kubectl create configmap -n "${namespace}" --from-file=testdata/catalogs/test-catalog-${tag} operator-controller-e2e-${tag}.build-contents kubectl apply -f - << EOF apiVersion: batch/v1 kind: Job metadata: - name: kaniko + name: "kaniko-${tag}" namespace: "${namespace}" spec: template: spec: containers: - - name: kaniko + - name: kaniko-${tag} image: gcr.io/kaniko-project/executor:latest - args: ["--dockerfile=/workspace/test-catalog.Dockerfile", + args: ["--dockerfile=/workspace/test-catalog-${tag}.Dockerfile", "--context=/workspace/", - "--destination=${tag}", + "--destination=${image}", "--skip-tls-verify"] volumeMounts: - name: dockerfile mountPath: /workspace/ - name: build-contents - mountPath: /workspace/test-catalog/ + mountPath: /workspace/test-catalog-${tag}/ restartPolicy: Never volumes: - name: dockerfile configMap: - name: operator-controller-e2e.dockerfile + name: operator-controller-e2e-${tag}.dockerfile items: - - key: test-catalog.Dockerfile - path: test-catalog.Dockerfile + - key: test-catalog-${tag}.Dockerfile + path: test-catalog-${tag}.Dockerfile - name: build-contents configMap: - name: operator-controller-e2e.build-contents + name: operator-controller-e2e-${tag}.build-contents EOF -kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko --timeout=60s +kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko-${tag} --timeout=60s diff --git a/testdata/catalogs/test-catalog.Dockerfile b/testdata/catalogs/test-catalog-v1.Dockerfile similarity index 84% rename from testdata/catalogs/test-catalog.Dockerfile rename to testdata/catalogs/test-catalog-v1.Dockerfile index efe922e05..d255e0774 100644 --- a/testdata/catalogs/test-catalog.Dockerfile +++ b/testdata/catalogs/test-catalog-v1.Dockerfile @@ -1,5 +1,5 @@ FROM scratch -ADD test-catalog /configs +ADD test-catalog-v1 /configs # Set DC-specific label for the location of the DC root directory # in the image diff --git a/testdata/catalogs/test-catalog/.indexignore b/testdata/catalogs/test-catalog-v1/.indexignore similarity index 100% rename from testdata/catalogs/test-catalog/.indexignore rename to testdata/catalogs/test-catalog-v1/.indexignore diff --git a/testdata/catalogs/test-catalog/catalog.yaml b/testdata/catalogs/test-catalog-v1/catalog.yaml similarity index 77% rename from testdata/catalogs/test-catalog/catalog.yaml rename to testdata/catalogs/test-catalog-v1/catalog.yaml index 36736dac2..9ec6beae5 100644 --- a/testdata/catalogs/test-catalog/catalog.yaml +++ b/testdata/catalogs/test-catalog-v1/catalog.yaml @@ -18,8 +18,6 @@ entries: replaces: prometheus-operator.1.0.0 - name: prometheus-operator.1.2.0 replaces: prometheus-operator.1.0.1 - - name: prometheus-operator.2.0.0 - replaces: prometheus-operator.1.2.0 --- schema: olm.bundle name: prometheus-operator.1.0.0 @@ -50,13 +48,3 @@ properties: value: packageName: prometheus version: 1.2.0 ---- -schema: olm.bundle -name: prometheus-operator.2.0.0 -package: prometheus -image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v2.0.0 -properties: - - type: olm.package - value: - packageName: prometheus - version: 2.0.0 diff --git a/testdata/catalogs/test-catalog-v2.Dockerfile b/testdata/catalogs/test-catalog-v2.Dockerfile new file mode 100644 index 000000000..72b3a7a2e --- /dev/null +++ b/testdata/catalogs/test-catalog-v2.Dockerfile @@ -0,0 +1,6 @@ +FROM scratch +ADD test-catalog-v2 /configs + +# Set DC-specific label for the location of the DC root directory +# in the image +LABEL operators.operatorframework.io.index.configs.v1=/configs diff --git a/testdata/catalogs/test-catalog-v2/.indexignore b/testdata/catalogs/test-catalog-v2/.indexignore new file mode 100644 index 000000000..699fa6d33 --- /dev/null +++ b/testdata/catalogs/test-catalog-v2/.indexignore @@ -0,0 +1,2 @@ +/expected_all.json +..* diff --git a/testdata/catalogs/test-catalog-v2/catalog.yaml b/testdata/catalogs/test-catalog-v2/catalog.yaml new file mode 100644 index 000000000..7208809cc --- /dev/null +++ b/testdata/catalogs/test-catalog-v2/catalog.yaml @@ -0,0 +1,21 @@ +--- +schema: olm.package +name: prometheus +defaultChannel: beta +--- +schema: olm.channel +name: beta +package: prometheus +entries: + - name: prometheus-operator.2.0.0 + replaces: prometheus-operator.1.2.0 +--- +schema: olm.bundle +name: prometheus-operator.2.0.0 +package: prometheus +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/prometheus-operator:v2.0.0 +properties: + - type: olm.package + value: + packageName: prometheus + version: 2.0.0