github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/packagemanifest_e2e_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  
     8  	"github.com/blang/semver/v4"
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  	opver "github.com/operator-framework/api/pkg/lib/version"
    12  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    13  	corev1 "k8s.io/api/core/v1"
    14  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/client-go/rest"
    17  
    18  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
    19  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
    20  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
    21  	packagev1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
    22  	pmversioned "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/client/clientset/versioned"
    23  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    24  )
    25  
    26  var _ = Describe("Package Manifest API lists available Operators from Catalog Sources", func() {
    27  	var (
    28  		crc                versioned.Interface
    29  		pmc                pmversioned.Interface
    30  		c                  operatorclient.ClientInterface
    31  		generatedNamespace corev1.Namespace
    32  	)
    33  
    34  	BeforeEach(func() {
    35  		crc = ctx.Ctx().OperatorClient()
    36  		pmc = newPMClient()
    37  		c = ctx.Ctx().KubeClient()
    38  
    39  		generatedNamespace = SetupGeneratedTestNamespace(genName("package-manifest-e2e"))
    40  	})
    41  
    42  	AfterEach(func() {
    43  		TearDown(generatedNamespace.GetName())
    44  	})
    45  
    46  	Context("Given a CatalogSource created using the ConfigMap as catalog source type", func() {
    47  		var (
    48  			catsrcName           string
    49  			packageName          string
    50  			packageAlpha         string
    51  			alphaChannel         string
    52  			packageStable        string
    53  			stableChannel        string
    54  			csvAlpha             v1alpha1.ClusterServiceVersion
    55  			csv                  v1alpha1.ClusterServiceVersion
    56  			cleanupCatalogSource cleanupFunc
    57  		)
    58  
    59  		BeforeEach(func() {
    60  			By(`create a simple catalogsource`)
    61  			packageName = genName("nginx")
    62  			alphaChannel = "alpha"
    63  			packageAlpha = packageName + "-alpha"
    64  			stableChannel = "stable"
    65  			packageStable = packageName + "-stable"
    66  			manifests := []registry.PackageManifest{
    67  				{
    68  					PackageName: packageName,
    69  					Channels: []registry.PackageChannel{
    70  						{Name: alphaChannel, CurrentCSVName: packageAlpha},
    71  						{Name: stableChannel, CurrentCSVName: packageStable},
    72  					},
    73  					DefaultChannelName: stableChannel,
    74  				},
    75  			}
    76  
    77  			crdPlural := genName("ins")
    78  			crd := newCRD(crdPlural)
    79  			catsrcName = genName("mock-ocs")
    80  			csv = newCSV(packageStable, generatedNamespace.GetName(), "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, nil)
    81  			csv.SetLabels(map[string]string{"projected": "label"})
    82  			csv.Spec.Keywords = []string{"foo", "bar"}
    83  			csv.Spec.Links = []v1alpha1.AppLink{
    84  				{
    85  					Name: "foo",
    86  					URL:  "example.com",
    87  				},
    88  			}
    89  			csv.Spec.Maintainers = []v1alpha1.Maintainer{
    90  				{
    91  					Name:  "foo",
    92  					Email: "example@gmail.com",
    93  				},
    94  			}
    95  			csv.Spec.Maturity = "foo"
    96  			csv.Spec.NativeAPIs = []metav1.GroupVersionKind{{Group: "kubenative.io", Version: "v1", Kind: "Native"}}
    97  			csv.Spec.Icon = []v1alpha1.Icon{
    98  				{
    99  					Data:      "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",
   100  					MediaType: "image/png",
   101  				},
   102  			}
   103  
   104  			csvAlpha = *csv.DeepCopy()
   105  			csvAlpha.SetName(packageAlpha)
   106  			csvAlpha.Spec.Version = opver.OperatorVersion{Version: semver.MustParse("0.1.1")}
   107  			csvAlpha.Spec.Replaces = csv.GetName()
   108  			csvAlpha.Spec.Icon = []v1alpha1.Icon{
   109  				{
   110  					Data:      base64.StdEncoding.EncodeToString([]byte(csvAlpha.GetName())),
   111  					MediaType: "image/png",
   112  				},
   113  			}
   114  
   115  			_, cleanupCatalogSource = createInternalCatalogSource(c, crc, catsrcName, generatedNamespace.GetName(), manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv, csvAlpha})
   116  
   117  			By(`Verify catalog source was created`)
   118  			_, err := fetchCatalogSourceOnStatus(crc, catsrcName, generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   119  			Expect(err).ToNot(HaveOccurred())
   120  		})
   121  
   122  		AfterEach(func() {
   123  			if cleanupCatalogSource != nil {
   124  				cleanupCatalogSource()
   125  			}
   126  		})
   127  
   128  		It("retrieves the PackageManifest by package name and validates its fields", func() {
   129  			By(`Drop icons to account for pruning`)
   130  			csvAlpha.Spec.Icon = nil
   131  			csv.Spec.Icon = nil
   132  
   133  			csvAlphaJSON, err := json.Marshal(csvAlpha)
   134  			Expect(err).ToNot(HaveOccurred())
   135  			csvJSON, err := json.Marshal(csv)
   136  			Expect(err).ToNot(HaveOccurred())
   137  
   138  			expectedStatus := packagev1.PackageManifestStatus{
   139  				CatalogSource:          catsrcName,
   140  				CatalogSourceNamespace: generatedNamespace.GetName(),
   141  				PackageName:            packageName,
   142  				Channels: []packagev1.PackageChannel{
   143  					{
   144  						Name:           alphaChannel,
   145  						CurrentCSV:     packageAlpha,
   146  						CurrentCSVDesc: packagev1.CreateCSVDescription(&csvAlpha, string(csvAlphaJSON)),
   147  						Entries: []packagev1.ChannelEntry{
   148  							{
   149  								Name:    csvAlpha.Name,
   150  								Version: csvAlpha.Spec.Version.String(),
   151  							},
   152  							{
   153  								Name:    csv.Name,
   154  								Version: csv.Spec.Version.String(),
   155  							},
   156  						},
   157  					},
   158  					{
   159  						Name:           stableChannel,
   160  						CurrentCSV:     packageStable,
   161  						CurrentCSVDesc: packagev1.CreateCSVDescription(&csv, string(csvJSON)),
   162  						Entries: []packagev1.ChannelEntry{
   163  							{
   164  								Name:    csv.Name,
   165  								Version: csv.Spec.Version.String(),
   166  							},
   167  						},
   168  					},
   169  				},
   170  				DefaultChannel: stableChannel,
   171  			}
   172  
   173  			pm, err := fetchPackageManifest(pmc, generatedNamespace.GetName(), packageName, packageManifestHasStatus)
   174  			Expect(err).ToNot(HaveOccurred(), "error getting package manifest")
   175  			Expect(pm).ShouldNot(BeNil())
   176  			Expect(pm.GetName()).Should(Equal(packageName))
   177  			Expect(pm.Status).Should(Equal(expectedStatus))
   178  			Expect(pm.GetLabels()["projected"]).Should(Equal("label"))
   179  			Expect(pm.GetLabels()["operatorframework.io/arch.amd64"]).Should(Equal("supported"))
   180  			Expect(pm.GetLabels()["operatorframework.io/os.linux"]).Should(Equal("supported"))
   181  		})
   182  
   183  		It("lists PackageManifest and ensures it has valid PackageManifest item", func() {
   184  			By(`Get a PackageManifestList and ensure it has the correct items`)
   185  			Eventually(func() (bool, error) {
   186  				pmList, err := pmc.OperatorsV1().PackageManifests(generatedNamespace.GetName()).List(context.TODO(), metav1.ListOptions{})
   187  				return containsPackageManifest(pmList.Items, packageName), err
   188  			}).Should(BeTrue(), "required package name not found in the list")
   189  		})
   190  
   191  		It("gets the icon from the default channel", func() {
   192  			var res rest.Result
   193  			Eventually(func() error {
   194  				res = pmc.OperatorsV1().RESTClient().Get().Resource("packagemanifests").SubResource("icon").Namespace(generatedNamespace.GetName()).Name(packageName).Do(context.Background())
   195  				return res.Error()
   196  			}).Should(Succeed(), "error getting icon")
   197  
   198  			data, err := res.Raw()
   199  			Expect(err).ToNot(HaveOccurred())
   200  
   201  			By(`Match against icon from the default`)
   202  			expected, err := base64.StdEncoding.DecodeString(csv.Spec.Icon[0].Data)
   203  			Expect(err).ToNot(HaveOccurred())
   204  			Expect(data).To(Equal(expected))
   205  		})
   206  	})
   207  
   208  	Context("Given a CatalogSource created using gRPC catalog source type", func() {
   209  		var (
   210  			packageName, displayName string
   211  			catalogSource            *v1alpha1.CatalogSource
   212  		)
   213  
   214  		BeforeEach(func() {
   215  			sourceName := genName("catalog-")
   216  			packageName = "etcd-test"
   217  			displayName = "etcd test catalog"
   218  			image := "quay.io/olmtest/catsrc-update-test:related"
   219  
   220  			catalogSource = &v1alpha1.CatalogSource{
   221  				TypeMeta: metav1.TypeMeta{
   222  					Kind:       v1alpha1.CatalogSourceKind,
   223  					APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
   224  				},
   225  				ObjectMeta: metav1.ObjectMeta{
   226  					Name:      sourceName,
   227  					Namespace: generatedNamespace.GetName(),
   228  					Labels:    map[string]string{"olm.catalogSource": sourceName},
   229  				},
   230  				Spec: v1alpha1.CatalogSourceSpec{
   231  					SourceType:  v1alpha1.SourceTypeGrpc,
   232  					Image:       image,
   233  					DisplayName: displayName,
   234  					GrpcPodConfig: &v1alpha1.GrpcPodConfig{
   235  						SecurityContextConfig: v1alpha1.Restricted,
   236  					},
   237  				},
   238  			}
   239  
   240  			var err error
   241  			catalogSource, err = crc.OperatorsV1alpha1().CatalogSources(catalogSource.GetNamespace()).Create(context.TODO(), catalogSource, metav1.CreateOptions{})
   242  			Expect(err).NotTo(HaveOccurred())
   243  
   244  			By(`Wait for the CatalogSource to be ready`)
   245  			_, err = fetchCatalogSourceOnStatus(crc, catalogSource.GetName(), catalogSource.GetNamespace(), catalogSourceRegistryPodSynced())
   246  			Expect(err).ToNot(HaveOccurred(), "catalog source did not become ready")
   247  		})
   248  
   249  		AfterEach(func() {
   250  			err := crc.OperatorsV1alpha1().CatalogSources(catalogSource.GetNamespace()).Delete(context.TODO(), catalogSource.GetName(), metav1.DeleteOptions{})
   251  			Expect(err).NotTo(HaveOccurred())
   252  		})
   253  
   254  		It("lists the CatalogSource contents using the PackageManifest API", func() {
   255  
   256  			pm, err := fetchPackageManifest(pmc, generatedNamespace.GetName(), packageName, packageManifestHasStatus)
   257  			Expect(err).NotTo(HaveOccurred(), "error getting package manifest")
   258  			Expect(pm).ShouldNot(BeNil())
   259  			Expect(pm.GetName()).Should(Equal(packageName))
   260  
   261  			By(`Verify related images from the package manifest`)
   262  			relatedImages := pm.Status.Channels[0].CurrentCSVDesc.RelatedImages
   263  
   264  			Expect(relatedImages).To(ConsistOf([]string{
   265  				"quay.io/coreos/etcd@sha256:3816b6daf9b66d6ced6f0f966314e2d4f894982c6b1493061502f8c2bf86ac84",
   266  				"quay.io/coreos/etcd@sha256:49d3d4a81e0d030d3f689e7167f23e120abf955f7d08dbedf3ea246485acee9f",
   267  				"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2",
   268  			}), "Expected images to exist in the related images list\n")
   269  		})
   270  
   271  		When("the display name for catalog source is updated", func() {
   272  
   273  			BeforeEach(func() {
   274  				pm, err := fetchPackageManifest(pmc, generatedNamespace.GetName(), packageName, packageManifestHasStatus)
   275  				Expect(err).NotTo(HaveOccurred(), "error getting package manifest")
   276  				Expect(pm).ShouldNot(BeNil())
   277  				Expect(pm.GetName()).Should(Equal(packageName))
   278  				Expect(pm.Status.CatalogSourceDisplayName).Should(Equal(displayName))
   279  
   280  				catalogSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Get(context.TODO(), catalogSource.GetName(), metav1.GetOptions{})
   281  				Expect(err).NotTo(HaveOccurred(), "error getting catalogSource")
   282  
   283  				displayName = "updated Name"
   284  				catalogSource.Spec.DisplayName = displayName
   285  				catalogSource, err = crc.OperatorsV1alpha1().CatalogSources(generatedNamespace.GetName()).Update(context.TODO(), catalogSource, metav1.UpdateOptions{})
   286  				Expect(err).NotTo(HaveOccurred(), "error updating catalogSource")
   287  				Expect(catalogSource.Spec.DisplayName).Should(Equal(displayName))
   288  			})
   289  
   290  			It("should successfully update the CatalogSource field", func() {
   291  
   292  				Eventually(func() (string, error) {
   293  					pm, err := fetchPackageManifest(pmc, generatedNamespace.GetName(), packageName,
   294  						packageManifestHasStatus)
   295  					if err != nil {
   296  						return "", err
   297  					}
   298  					return pm.Status.CatalogSourceDisplayName, nil
   299  				}).Should(Equal(displayName))
   300  			})
   301  		})
   302  	})
   303  })
   304  
   305  type packageManifestCheckFunc func(*packagev1.PackageManifest) bool
   306  
   307  func packageManifestHasStatus(pm *packagev1.PackageManifest) bool {
   308  	// as long as it has a package name we consider the status non-empty
   309  	return pm != nil && pm.Status.PackageName != ""
   310  }
   311  
   312  func fetchPackageManifest(pmc pmversioned.Interface, namespace, name string, check packageManifestCheckFunc) (*packagev1.PackageManifest, error) {
   313  	var fetched *packagev1.PackageManifest
   314  	var err error
   315  
   316  	EventuallyWithOffset(1, func() (bool, error) {
   317  		ctx.Ctx().Logf("Polling...")
   318  		fetched, err = pmc.OperatorsV1().PackageManifests(namespace).Get(context.TODO(), name, metav1.GetOptions{})
   319  		if err != nil {
   320  			return false, err
   321  		}
   322  		return check(fetched), nil
   323  	}).Should(BeTrue())
   324  
   325  	return fetched, err
   326  }
   327  
   328  func containsPackageManifest(pmList []packagev1.PackageManifest, pkgName string) bool {
   329  	for _, pm := range pmList {
   330  		if pm.GetName() == pkgName {
   331  			return true
   332  		}
   333  	}
   334  	return false
   335  }