diff --git a/internal/pkg/scaffold/olm-catalog/csv.go b/internal/pkg/scaffold/olm-catalog/csv.go index 379699db4a5..5d70d3d445c 100644 --- a/internal/pkg/scaffold/olm-catalog/csv.go +++ b/internal/pkg/scaffold/olm-catalog/csv.go @@ -357,6 +357,7 @@ func replaceAllBytes(v interface{}, old, new []byte) error { // user-defined manifests and updates csv. func (s *CSV) updateCSVFromManifestFiles(cfg *CSVConfig, csv *olmapiv1alpha1.ClusterServiceVersion) error { store := NewUpdaterStore() + otherSpecs := make(map[string][][]byte) for _, f := range append(cfg.CRDCRPaths, cfg.OperatorPath, cfg.RolePath) { yamlData, err := afero.ReadFile(s.getFS(), f) if err != nil { @@ -366,10 +367,33 @@ func (s *CSV) updateCSVFromManifestFiles(cfg *CSVConfig, csv *olmapiv1alpha1.Clu scanner := yamlutil.NewYAMLScanner(yamlData) for scanner.Scan() { yamlSpec := scanner.Bytes() - - if err = store.AddToUpdater(yamlSpec); err != nil { + kind, err := getKindfromYAML(yamlSpec) + if err != nil { + return err + } + found, err := store.AddToUpdater(yamlSpec, kind) + if err != nil { return err } + if !found { + if _, ok := otherSpecs[kind]; !ok { + otherSpecs[kind] = make([][]byte, 0) + } + otherSpecs[kind] = append(otherSpecs[kind], yamlSpec) + } + } + if err = scanner.Err(); err != nil { + return err + } + } + + for k := range store.crds.crKinds { + if crSpecs, ok := otherSpecs[k]; ok { + for _, spec := range crSpecs { + if err := store.AddCR(spec); err != nil { + return err + } + } } } diff --git a/internal/pkg/scaffold/olm-catalog/csv_updaters.go b/internal/pkg/scaffold/olm-catalog/csv_updaters.go index 5be5da135c1..6f5180327fc 100644 --- a/internal/pkg/scaffold/olm-catalog/csv_updaters.go +++ b/internal/pkg/scaffold/olm-catalog/csv_updaters.go @@ -17,6 +17,7 @@ package catalog import ( "encoding/json" "fmt" + "strings" "github.com/ghodss/yaml" olmapiv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" @@ -34,24 +35,28 @@ type CSVUpdater interface { } type updaterStore struct { - installStrategy *CSVInstallStrategyUpdate - crdUpdate *CSVCustomResourceDefinitionsUpdate + installStrategy *InstallStrategyUpdate + crds *CustomResourceDefinitionsUpdate + almExamples *ALMExamplesUpdate } func NewUpdaterStore() *updaterStore { return &updaterStore{ - installStrategy: &CSVInstallStrategyUpdate{ + installStrategy: &InstallStrategyUpdate{ &olminstall.StrategyDetailsDeployment{}, }, - crdUpdate: &CSVCustomResourceDefinitionsUpdate{ + crds: &CustomResourceDefinitionsUpdate{ &olmapiv1alpha1.CustomResourceDefinitions{}, + make(map[string]struct{}), }, + almExamples: &ALMExamplesUpdate{}, } } // Apply iteratively calls each stored CSVUpdater's Apply() method. func (s *updaterStore) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { - for _, updater := range []CSVUpdater{s.installStrategy, s.crdUpdate} { + updaters := []CSVUpdater{s.installStrategy, s.crds, s.almExamples} + for _, updater := range updaters { if err := updater.Apply(csv); err != nil { return err } @@ -60,7 +65,6 @@ func (s *updaterStore) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { } func getKindfromYAML(yamlData []byte) (string, error) { - // Get Kind for inital categorization. var temp struct { Kind string } @@ -70,27 +74,25 @@ func getKindfromYAML(yamlData []byte) (string, error) { return temp.Kind, nil } -func (s *updaterStore) AddToUpdater(yamlSpec []byte) error { - kind, err := getKindfromYAML(yamlSpec) - if err != nil { - return err - } - +func (s *updaterStore) AddToUpdater(yamlSpec []byte, kind string) (found bool, err error) { + found = true switch kind { case "Role": - return s.AddRole(yamlSpec) + err = s.AddRole(yamlSpec) case "ClusterRole": - return s.AddClusterRole(yamlSpec) + err = s.AddClusterRole(yamlSpec) case "Deployment": - return s.AddDeploymentSpec(yamlSpec) + err = s.AddDeploymentSpec(yamlSpec) case "CustomResourceDefinition": // All CRD's present will be 'owned'. - return s.AddOwnedCRD(yamlSpec) + err = s.AddOwnedCRD(yamlSpec) + default: + found = false } - return nil + return found, err } -type CSVInstallStrategyUpdate struct { +type InstallStrategyUpdate struct { *olminstall.StrategyDetailsDeployment } @@ -136,7 +138,7 @@ func (store *updaterStore) AddDeploymentSpec(yamlDoc []byte) error { return nil } -func (u *CSVInstallStrategyUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) (err error) { +func (u *InstallStrategyUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) (err error) { // Get install strategy from csv. Default to a deployment strategy if none found. var strat olminstall.Strategy if csv.Spec.InstallStrategy.StrategyName == "" { @@ -170,26 +172,27 @@ func (u *CSVInstallStrategyUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersi return nil } -func (u *CSVInstallStrategyUpdate) updatePermissions(strat *olminstall.StrategyDetailsDeployment) { +func (u *InstallStrategyUpdate) updatePermissions(strat *olminstall.StrategyDetailsDeployment) { if len(u.Permissions) != 0 { strat.Permissions = u.Permissions } } -func (u *CSVInstallStrategyUpdate) updateClusterPermissions(strat *olminstall.StrategyDetailsDeployment) { +func (u *InstallStrategyUpdate) updateClusterPermissions(strat *olminstall.StrategyDetailsDeployment) { if len(u.ClusterPermissions) != 0 { strat.ClusterPermissions = u.ClusterPermissions } } -func (u *CSVInstallStrategyUpdate) updateDeploymentSpecs(strat *olminstall.StrategyDetailsDeployment) { +func (u *InstallStrategyUpdate) updateDeploymentSpecs(strat *olminstall.StrategyDetailsDeployment) { if len(u.DeploymentSpecs) != 0 { strat.DeploymentSpecs = u.DeploymentSpecs } } -type CSVCustomResourceDefinitionsUpdate struct { +type CustomResourceDefinitionsUpdate struct { *olmapiv1alpha1.CustomResourceDefinitions + crKinds map[string]struct{} } func (store *updaterStore) AddOwnedCRD(yamlDoc []byte) error { @@ -197,17 +200,18 @@ func (store *updaterStore) AddOwnedCRD(yamlDoc []byte) error { if err := yaml.Unmarshal(yamlDoc, crd); err != nil { return err } - store.crdUpdate.Owned = append(store.crdUpdate.Owned, olmapiv1alpha1.CRDDescription{ + store.crds.Owned = append(store.crds.Owned, olmapiv1alpha1.CRDDescription{ Name: crd.ObjectMeta.Name, Version: crd.Spec.Version, Kind: crd.Spec.Names.Kind, }) + store.crds.crKinds[crd.Spec.Names.Kind] = struct{}{} return nil } // Apply updates csv's "owned" CRDDescriptions. "required" CRDDescriptions are // left as-is, since they are user-defined values. -func (u *CSVCustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { +func (u *CustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { set := make(map[string]olmapiv1alpha1.CRDDescription) for _, csvDesc := range csv.Spec.CustomResourceDefinitions.Owned { set[csvDesc.Name] = csvDesc @@ -224,3 +228,40 @@ func (u *CSVCustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterSe csv.Spec.CustomResourceDefinitions.Owned = du.Owned return nil } + +type ALMExamplesUpdate struct { + crs []string +} + +func (store *updaterStore) AddCR(yamlDoc []byte) error { + if len(yamlDoc) == 0 { + return nil + } + crBytes, err := yaml.YAMLToJSON(yamlDoc) + if err != nil { + return err + } + store.almExamples.crs = append(store.almExamples.crs, string(crBytes)) + return nil +} + +func (u *ALMExamplesUpdate) Apply(csv *olmapiv1alpha1.ClusterServiceVersion) error { + if len(u.crs) == 0 { + return nil + } + if csv.GetAnnotations() == nil { + csv.SetAnnotations(make(map[string]string)) + } + sb := &strings.Builder{} + sb.WriteString(`[`) + for i, example := range u.crs { + sb.WriteString(example) + if i < len(u.crs)-1 { + sb.WriteString(`,`) + } + } + sb.WriteString(`]`) + + csv.GetAnnotations()["alm-examples"] = sb.String() + return nil +} diff --git a/internal/pkg/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml b/internal/pkg/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml index bb77e10f55c..ba0fa84ed9d 100644 --- a/internal/pkg/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml +++ b/internal/pkg/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml @@ -2,6 +2,7 @@ apiVersion: operators.coreos.com/v1alpha1 kind: ClusterServiceVersion metadata: annotations: + alm-examples: '[{"apiVersion":"app.example.com/v1alpha1","kind":"App","metadata":{"name":"example-app"},"spec":{"size":3}}]' capabilities: Basic Install name: app-operator.v0.1.0 namespace: placeholder