From 2a912ae482612571b414dfd7d5cbfdd6db61f557 Mon Sep 17 00:00:00 2001 From: David Eads Date: Mon, 1 May 2023 14:53:41 -0400 Subject: [PATCH 1/2] allow consumption of featuregates from manifests --- pkg/cmd/render/render.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/render/render.go b/pkg/cmd/render/render.go index 04857d592..695f6be28 100644 --- a/pkg/cmd/render/render.go +++ b/pkg/cmd/render/render.go @@ -12,6 +12,7 @@ import ( kubecontrolplanev1 "github.com/openshift/api/kubecontrolplane/v1" "github.com/openshift/cluster-kube-controller-manager-operator/bindata" "github.com/openshift/cluster-kube-controller-manager-operator/pkg/operator/targetconfigcontroller" + "github.com/openshift/library-go/pkg/operator/configobserver/featuregates" genericrender "github.com/openshift/library-go/pkg/operator/render" genericrenderoptions "github.com/openshift/library-go/pkg/operator/render/options" "github.com/spf13/cobra" @@ -134,6 +135,23 @@ func setFeatureGates(renderConfig *TemplateData, opts *renderOpts) error { return nil } +func setFeatureGatesFromAccessor(renderConfig *TemplateData, featureGates featuregates.FeatureGateAccess) error { + currFeatureGates, err := featureGates.CurrentFeatureGates() + if err != nil { + return fmt.Errorf("unable to get FeatureGates: %w", err) + } + allGates := []string{} + for _, featureGateName := range currFeatureGates.KnownFeatures() { + if currFeatureGates.Enabled(featureGateName) { + allGates = append(allGates, fmt.Sprintf("%v=true", featureGateName)) + } else { + allGates = append(allGates, fmt.Sprintf("%v=false", featureGateName)) + } + } + renderConfig.FeatureGates = allGates + return nil +} + func discoverRestrictedCIDRs(clusterConfigFileData []byte, renderConfig *TemplateData) error { if err := discoverRestrictedCIDRsFromNetwork(clusterConfigFileData, renderConfig); err != nil { if err = discoverRestrictedCIDRsFromClusterAPI(clusterConfigFileData, renderConfig); err != nil { @@ -228,9 +246,20 @@ func (r *renderOpts) Run() error { return fmt.Errorf("unable to parse restricted CIDRs from config: %v", err) } } - if err := setFeatureGates(&renderConfig, r); err != nil { - return err + + featureGates, err := r.generic.FeatureGates() + if err != nil { + klog.Warningf(fmt.Sprintf("error getting FeatureGates: %v", err)) + if err := setFeatureGates(&renderConfig, r); err != nil { + return err + } + + } else { + if err := setFeatureGatesFromAccessor(&renderConfig, featureGates); err != nil { + return err + } } + if err := r.manifest.ApplyTo(&renderConfig.ManifestConfig); err != nil { return err } From e8c5cf21a068730261bf89defabd6b7bd6ef8c0a Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 2 May 2023 12:16:57 -0400 Subject: [PATCH 2/2] update library-go for consuming featuresets from manifests --- go.mod | 4 +- go.sum | 8 +- ...perator_01_infrastructure-Default.crd.yaml | 16 ++ .../v1/stable.infrastructure.testsuite.yaml | 163 ++++++++++++++++++ .../api/config/v1/types_infrastructure.go | 1 - .../pkg/operator/render/options/config.go | 147 ++++++++++++++-- .../pkg/operator/render/options/generic.go | 144 +++++++++++++--- .../library-go/pkg/operator/render/render.go | 7 +- vendor/modules.txt | 4 +- 9 files changed, 450 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index f5144b5a4..e787a4e1c 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/gonum/graph v0.0.0-20170401004347-50b27dea7ebb github.com/google/go-cmp v0.5.9 - github.com/openshift/api v0.0.0-20230426193520-54a14470e5dc + github.com/openshift/api v0.0.0-20230502141037-a22bd9995471 github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 - github.com/openshift/library-go v0.0.0-20230501134757-c3f6a3f540c5 + github.com/openshift/library-go v0.0.0-20230502160234-500059c0376f github.com/prometheus/client_golang v1.14.0 github.com/prometheus/common v0.37.0 github.com/spf13/cobra v1.6.0 diff --git a/go.sum b/go.sum index b56ca32d2..10e3c7504 100644 --- a/go.sum +++ b/go.sum @@ -424,14 +424,14 @@ github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= -github.com/openshift/api v0.0.0-20230426193520-54a14470e5dc h1:U/ran9ckjGV/aSVTrWHFa/DY2L4Y1PT5N0lSDe/4of4= -github.com/openshift/api v0.0.0-20230426193520-54a14470e5dc/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= +github.com/openshift/api v0.0.0-20230502141037-a22bd9995471 h1:3yy5gCHc/eFKRu+x7UwPZq/PVqqT31wnBa1LcT2x2v8= +github.com/openshift/api v0.0.0-20230502141037-a22bd9995471/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d h1:RR4ah7FfaPR1WePizm0jlrsbmPu91xQZnAsVVreQV1k= github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 h1:66uaqNwA+qYyQDwsMWUfjjau8ezmg1dzCqub13KZOcE= github.com/openshift/client-go v0.0.0-20230120202327-72f107311084/go.mod h1:M3h9m001PWac3eAudGG3isUud6yBjr5XpzLYLLTlHKo= -github.com/openshift/library-go v0.0.0-20230501134757-c3f6a3f540c5 h1:g4rKtkEhi/nqBtiuKQ3JamwLT7/Pdb5NC/PfhbAyQcM= -github.com/openshift/library-go v0.0.0-20230501134757-c3f6a3f540c5/go.mod h1:JLv17uWyQvoK14yxp7h2vBJ8zz2xkWqanOv5TzuFkvY= +github.com/openshift/library-go v0.0.0-20230502160234-500059c0376f h1:pdGIBN2C2pm4ckYQfjMSSsdPgRG/lpvt3kuI0APY6T0= +github.com/openshift/library-go v0.0.0-20230502160234-500059c0376f/go.mod h1:JLv17uWyQvoK14yxp7h2vBJ8zz2xkWqanOv5TzuFkvY= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure-Default.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure-Default.crd.yaml index 8a449e843..bb8817110 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure-Default.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_infrastructure-Default.crd.yaml @@ -682,6 +682,22 @@ spec: type: string maxItems: 2 type: array + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used by the cluster on OpenStack platform which can be a user-managed or openshift-managed load balancer that is to be used for the OpenShift API and Ingress endpoints. When set to OpenShiftManagedDefault the static pods in charge of API and Ingress traffic load-balancing defined in the machine config operator will be deployed. When set to UserManaged these static pods will not be deployed and it is expected that the load balancer is configured out of band by the deployer. When omitted, this means no opinion and the platform is left to choose a reasonable default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object nodeDNSIP: description: nodeDNSIP is the IP address for the internal DNS used by the nodes. Unlike the one managed by the DNS operator, `NodeDNSIP` provides name resolution for the nodes themselves. There is no DNS-as-a-service for OpenStack deployments. In order to minimize necessary changes to the datacenter DNS, a DNS service is hosted as a static pod to serve those hostnames to the nodes in the cluster. type: string diff --git a/vendor/github.com/openshift/api/config/v1/stable.infrastructure.testsuite.yaml b/vendor/github.com/openshift/api/config/v1/stable.infrastructure.testsuite.yaml index 089699262..4266122b0 100644 --- a/vendor/github.com/openshift/api/config/v1/stable.infrastructure.testsuite.yaml +++ b/vendor/github.com/openshift/api/config/v1/stable.infrastructure.testsuite.yaml @@ -311,3 +311,166 @@ tests: powervs: region: some-region expectedStatusError: "status.platformStatus.powervs: Invalid value: \"object\": cannot unset resourceGroup once set" + - name: Should set load balancer type to OpenShiftManagedDefault if not specified + initial: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + updated: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + platform: OpenStack + platformStatus: + openstack: {} + type: OpenStack + expected: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: OpenShiftManagedDefault + type: OpenStack + - name: Should be able to override the default load balancer with a valid value + initial: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + updated: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: UserManaged + type: OpenStack + expected: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: UserManaged + type: OpenStack + - name: Should not allow changing the immutable load balancer type field + initial: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: OpenShiftManagedDefault + type: OpenStack + updated: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + type: OpenStack + openstack: {} + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: UserManaged + type: OpenStack + expectedStatusError: "status.platformStatus.openstack.loadBalancer.type: Invalid value: \"string\": type is immutable once set" + - name: Should not allow removing the immutable load balancer type field that was initially set + initial: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: UserManaged + type: OpenStack + updated: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + type: OpenStack + openstack: {} + status: + controlPlaneTopology: HighlyAvailable + infrastructureTopology: HighlyAvailable + platform: OpenStack + platformStatus: + openstack: {} + type: OpenStack + expectedStatusError: "status.platformStatus.openstack.loadBalancer.type: Invalid value: \"string\": type is immutable once set" + - name: Should not allow setting the load balancer type to a wrong value + initial: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + updated: | + apiVersion: config.openshift.io/v1 + kind: Infrastructure + spec: + platformSpec: + openstack: {} + type: OpenStack + status: + platform: OpenStack + platformStatus: + openstack: + loadBalancer: + type: FooBar + type: OpenStack + expectedStatusError: "status.platformStatus.openstack.loadBalancer.type: Unsupported value: \"FooBar\": supported values: \"OpenShiftManagedDefault\", \"UserManaged\"" diff --git a/vendor/github.com/openshift/api/config/v1/types_infrastructure.go b/vendor/github.com/openshift/api/config/v1/types_infrastructure.go index c3a9ab79b..d47acdb77 100644 --- a/vendor/github.com/openshift/api/config/v1/types_infrastructure.go +++ b/vendor/github.com/openshift/api/config/v1/types_infrastructure.go @@ -701,7 +701,6 @@ type OpenStackPlatformStatus struct { // loadBalancer defines how the load balancer used by the cluster is configured. // +default={"type": "OpenShiftManagedDefault"} // +kubebuilder:default={"type": "OpenShiftManagedDefault"} - // +openshift:enable:FeatureSets=TechPreviewNoUpgrade // +optional LoadBalancer *OpenStackPlatformLoadBalancer `json:"loadBalancer,omitempty"` } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/render/options/config.go b/vendor/github.com/openshift/library-go/pkg/operator/render/options/config.go index 8aa28116e..2f016f5b5 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/render/options/config.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/render/options/config.go @@ -1,7 +1,16 @@ package options import ( + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + + utilerrors "k8s.io/apimachinery/pkg/util/errors" + + "k8s.io/apimachinery/pkg/api/meta" + "github.com/openshift/library-go/pkg/operator/resource/resourceread" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" @@ -34,19 +43,16 @@ type ManifestConfig struct { ImagePullPolicy string } -// FileConfig type FileConfig struct { // BootstrapConfig holds the rendered control plane component config file for bootstrapping (phase 1). BootstrapConfig []byte // Assets holds the loaded assets like certs and keys. Assets map[string][]byte - - // RenderedManifests are the files, content, and (optionally) decoded objects that were passed to the command - // as already present to be created by cluster-bootstrap. - RenderedManifests []RenderedManifest } +type RenderedManifests []RenderedManifest + type RenderedManifest struct { OriginalFilename string Content []byte @@ -60,29 +66,148 @@ type TemplateData struct { FileConfig } -func (c *FileConfig) ListManifestOfType(gvk schema.GroupVersionKind) []RenderedManifest { +func (renderedManifests RenderedManifests) ListManifestOfType(gvk schema.GroupVersionKind) []RenderedManifest { ret := []RenderedManifest{} - for i := range c.RenderedManifests { - obj, err := c.RenderedManifests[i].GetDecodedObj() + for i := range renderedManifests { + obj, err := renderedManifests[i].GetDecodedObj() if err != nil { - klog.Warningf("failure to read %q: %v", c.RenderedManifests[i].OriginalFilename, err) + klog.Warningf("failure to read %q: %v", renderedManifests[i].OriginalFilename, err) continue } if obj.GetObjectKind().GroupVersionKind() == gvk { - ret = append(ret, c.RenderedManifests[i]) + ret = append(ret, renderedManifests[i]) } } return ret } +func (renderedManifests RenderedManifests) GetManifest(gvk schema.GroupVersionKind, namespace, name string) (RenderedManifest, error) { + for i := range renderedManifests { + obj, err := renderedManifests[i].GetDecodedObj() + if err != nil { + klog.Warningf("failure to read %q: %v", renderedManifests[i].OriginalFilename, err) + continue + } + if obj.GetObjectKind().GroupVersionKind() != gvk { + continue + } + objMetadata, err := meta.Accessor(obj) + if err != nil { + klog.Warningf("failure to read metadata %q: %v", renderedManifests[i].OriginalFilename, err) + continue + } + + // since validation requires that all of these are the same, it doesn't matterwhich one we return + if objMetadata.GetName() == name && objMetadata.GetNamespace() == namespace { + return renderedManifests[i], nil + } + } + + return RenderedManifest{}, apierrors.NewNotFound( + schema.GroupResource{ + Group: gvk.Group, + Resource: gvk.Kind, + }, + name) +} + +func (renderedManifests RenderedManifests) GetObject(gvk schema.GroupVersionKind, namespace, name string) (runtime.Object, error) { + manifest, err := renderedManifests.GetManifest(gvk, namespace, name) + if err != nil { + return nil, err + } + return manifest.decodedObj, nil +} + +func (renderedManifests RenderedManifests) ValidateManifestPredictability() error { + errs := []error{} + decodeErrorsObserved := map[int]bool{} + metadataErrorsObserved := map[int]bool{} + + type compareTuple struct { + i, j int + } + compareTuplesErrorsObserved := map[compareTuple]bool{} + + for i := range renderedManifests { + lhs := renderedManifests[i] + lhsObj, err := lhs.GetDecodedObj() + if err != nil { + if !decodeErrorsObserved[i] { + errs = append(errs, err) + decodeErrorsObserved[i] = true + } + continue + } + lhsMetadata, err := meta.Accessor(lhsObj) + if err != nil { + if !metadataErrorsObserved[i] { + errs = append(errs, fmt.Errorf("unable to read metadata for %q: %w", lhs.OriginalFilename, err)) + metadataErrorsObserved[i] = true + } + continue + } + + for j := range renderedManifests { + if i == j { + continue + } + rhs := renderedManifests[j] + rhsObj, err := rhs.GetDecodedObj() + if err != nil { + if !decodeErrorsObserved[j] { + errs = append(errs, err) + decodeErrorsObserved[j] = true + } + continue + } + rhsMetadata, err := meta.Accessor(rhsObj) + if err != nil { + if !metadataErrorsObserved[j] { + errs = append(errs, fmt.Errorf("unable to read metadata for %q: %w", rhs.OriginalFilename, err)) + metadataErrorsObserved[j] = true + } + continue + } + if lhsObj.GetObjectKind().GroupVersionKind().GroupKind() != rhsObj.GetObjectKind().GroupVersionKind().GroupKind() { + continue + } + if lhsMetadata.GetName() != rhsMetadata.GetName() { + continue + } + if lhsMetadata.GetNamespace() != rhsMetadata.GetNamespace() { + continue + } + + if !equality.Semantic.DeepEqual(lhsObj, rhsObj) { + if !compareTuplesErrorsObserved[compareTuple{i, j}] { + errs = append(errs, + fmt.Errorf("%q and %q both set %v.%v/%v in ns/%v, but have different values", + lhs.OriginalFilename, + rhs.OriginalFilename, + lhsObj.GetObjectKind().GroupVersionKind().Kind, + lhsObj.GetObjectKind().GroupVersionKind().Group, + lhsMetadata.GetName(), + lhsMetadata.GetNamespace(), + )) + compareTuplesErrorsObserved[compareTuple{i, j}] = true + compareTuplesErrorsObserved[compareTuple{j, i}] = true + } + } + } + } + + return utilerrors.NewAggregate(errs) +} + func (c *RenderedManifest) GetDecodedObj() (runtime.Object, error) { if c.decodedObj != nil { return c.decodedObj, nil } obj, err := resourceread.ReadGenericWithUnstructured(c.Content) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to decode %q: %w", c.OriginalFilename, err) } c.decodedObj = obj diff --git a/vendor/github.com/openshift/library-go/pkg/operator/render/options/generic.go b/vendor/github.com/openshift/library-go/pkg/operator/render/options/generic.go index 48d44607c..d9ae99fe8 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/render/options/generic.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/render/options/generic.go @@ -5,17 +5,16 @@ import ( "errors" "fmt" "io/ioutil" - "os" "text/template" "github.com/ghodss/yaml" - "github.com/spf13/pflag" - - "k8s.io/apimachinery/pkg/runtime/schema" - configv1 "github.com/openshift/api/config/v1" "github.com/openshift/library-go/pkg/assets" + "github.com/openshift/library-go/pkg/operator/configobserver/featuregates" "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" + "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime/schema" ) // GenericOptions contains the generic render command options. @@ -31,7 +30,8 @@ type GenericOptions struct { AssetInputDir string AssetOutputDir string - FeatureSet string + FeatureSet string + PayloadVersion string } type Template struct { @@ -57,6 +57,8 @@ func (o *GenericOptions) AddFlags(fs *pflag.FlagSet, configGVK schema.GroupVersi fs.StringVar(&o.FeatureSet, "feature-set", o.FeatureSet, "Enables features that are not part of the default feature set.") fs.StringSliceVar(&o.RenderedManifestInputFilenames, "rendered-manifest-files", o.RenderedManifestInputFilenames, "files or directories containing yaml or json manifests that will be created via cluster-bootstrapping.") + fs.StringVar(&o.PayloadVersion, "payload-version", o.PayloadVersion, "Version that will eventually be placed into ClusterOperator.status. This normally comes from the CVO set via env var: OPERATOR_IMAGE_VERSION.") + } type gvkOutput struct { @@ -87,10 +89,11 @@ func (o *GenericOptions) Validate() error { return errors.New("missing required flag: --config-output-file") } - for _, filename := range o.RenderedManifestInputFilenames { - _, err := os.Stat(filename) - if err != nil { - return fmt.Errorf("--rendered-manifest-files, value %q could not be read: %v", filename, err) + if renderedManifests, err := o.ReadInputManifests(); err != nil { + return fmt.Errorf("--rendered-manifest-files, could not be read: %v", err) + } else { + if err := renderedManifests.ValidateManifestPredictability(); err != nil { + return fmt.Errorf("--rendered-manifest-files, are not consistent so results would be unpredictable depending on apply order: %v", err) } } @@ -99,9 +102,117 @@ func (o *GenericOptions) Validate() error { default: return fmt.Errorf("invalid feature-set specified: %q", o.FeatureSet) } + return nil } +func (o *GenericOptions) ReadInputManifests() (RenderedManifests, error) { + ret := RenderedManifests{} + for _, filename := range o.RenderedManifestInputFilenames { + manifestContent, err := assets.LoadFilesRecursively(filename) + if err != nil { + return nil, fmt.Errorf("failed loading rendered manifest inputs from %q: %w", filename, err) + } + for manifestFile, content := range manifestContent { + ret = append(ret, RenderedManifest{ + OriginalFilename: manifestFile, + Content: content, + }) + } + } + + return ret, nil +} + +func (o *GenericOptions) FeatureGates() (featuregates.FeatureGateAccess, error) { + if len(o.PayloadVersion) == 0 { + return nil, fmt.Errorf("cannot return FeatureGate without payload version") + } + if len(o.RenderedManifestInputFilenames) == 0 { + return nil, fmt.Errorf("cannot return FeatureGate without rendered manifests") + } + + manifests, err := o.FeatureGateManifests() + if err != nil { + return nil, fmt.Errorf("error reading input manifests: %w", err) + } + // they're all the same, so just get the first + featureGate, err := manifests[0].GetDecodedObj() + if err != nil { + return nil, fmt.Errorf("error decoding FeatureGates: %w", err) + } + + ret, err := featuregates.NewHardcodedFeatureGateAccessFromFeatureGate(featureGate.(*configv1.FeatureGate), o.PayloadVersion) + if err != nil { + return nil, fmt.Errorf("error creating feature accessor: %w", err) + } + + return ret, nil +} + +// FeatureGateManifests is exposed for usage in getting FeatureGateAccess and for convenient by cluster-config-operator +func (o *GenericOptions) FeatureGateManifests() (RenderedManifests, error) { + if len(o.RenderedManifestInputFilenames) == 0 { + return nil, nil + } + + inputManifest, err := o.ReadInputManifests() + if err != nil { + return nil, fmt.Errorf("error reading input manifests: %w", err) + } + featureGates := inputManifest.ListManifestOfType(configv1.GroupVersion.WithKind("FeatureGate")) + if len(featureGates) == 0 { + return nil, fmt.Errorf("no FeatureGates found in manfest dir: %v", o.RenderedManifestInputFilenames) + } + + ret := RenderedManifests{} + + var prev *RenderedManifest + var featureGate *configv1.FeatureGate + for i := range featureGates { + curr := featureGates[i] + ret = append(ret, curr) + + decodedObj, err := curr.GetDecodedObj() + if err != nil { + return nil, fmt.Errorf("decoding failure for %q: %w", curr.OriginalFilename, err) + } + currFeatureGate, ok := decodedObj.(*configv1.FeatureGate) + if !ok { + return nil, fmt.Errorf("wrong obj type for %q: %T: %v", curr.OriginalFilename, decodedObj, curr.Content) + } + if featureGate == nil { + prev = &curr + featureGate = currFeatureGate + continue + } + + if !equality.Semantic.DeepEqual(featureGate, currFeatureGate) { + return nil, fmt.Errorf("FeatureGate manifests disagree: %q and %q, with \n%v\n%v ", prev.OriginalFilename, curr.OriginalFilename, prev.Content, curr.Content) + } + } + + return ret, nil +} + +func (o *GenericOptions) FeatureSetName() (configv1.FeatureSet, error) { + if len(o.RenderedManifestInputFilenames) == 0 { + return configv1.FeatureSet(o.FeatureSet), nil + } + + manifests, err := o.FeatureGateManifests() + if err != nil { + return "MISSING", fmt.Errorf("error reading input manifests: %w", err) + } + // they're all the same, so just get the first + featureGate, err := manifests[0].GetDecodedObj() + if err != nil { + return "MISSING", fmt.Errorf("error decoding FeatureGates: %w", err) + } + + return featureGate.(*configv1.FeatureGate).Spec.FeatureSet, nil +} + // ApplyTo applies the options to the given config struct using the provided text/template data. func (o *GenericOptions) ApplyTo(cfg *FileConfig, defaultConfig, bootstrapOverrides Template, templateData interface{}, specialCases map[string]resourcemerge.MergeFunc) error { var err error @@ -116,19 +227,6 @@ func (o *GenericOptions) ApplyTo(cfg *FileConfig, defaultConfig, bootstrapOverri return fmt.Errorf("failed loading assets from %q: %v", o.AssetInputDir, err) } - for _, filename := range o.RenderedManifestInputFilenames { - manifestContent, err := assets.LoadFilesRecursively(filename) - if err != nil { - return fmt.Errorf("failed loading rendered manifest inputs from %q: %w", filename, err) - } - for manifestFile, content := range manifestContent { - cfg.RenderedManifests = append(cfg.RenderedManifests, RenderedManifest{ - OriginalFilename: manifestFile, - Content: content, - }) - } - } - return nil } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/render/render.go b/vendor/github.com/openshift/library-go/pkg/operator/render/render.go index 9c772a56c..adffee5c3 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/render/render.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/render/render.go @@ -11,8 +11,13 @@ import ( // WriteFiles writes the manifests and the bootstrap config file. func WriteFiles(opt *options.GenericOptions, fileConfig *options.FileConfig, templateData interface{}, additionalPredicates ...assets.FileInfoPredicate) error { + featureSet, err := opt.FeatureSetName() + if err != nil { + return err + } + defaultPredicates := []assets.FileInfoPredicate{assets.OnlyYaml} - manifestPredicates := []assets.FileContentsPredicate{assets.InstallerFeatureSet(opt.FeatureSet)} + manifestPredicates := []assets.FileContentsPredicate{assets.InstallerFeatureSet(string(featureSet))} // write assets for _, manifestDir := range []string{"bootstrap-manifests", "manifests"} { diff --git a/vendor/modules.txt b/vendor/modules.txt index 821b04add..ab51fb7f1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -192,7 +192,7 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg -# github.com/openshift/api v0.0.0-20230426193520-54a14470e5dc +# github.com/openshift/api v0.0.0-20230502141037-a22bd9995471 ## explicit; go 1.19 github.com/openshift/api github.com/openshift/api/apiserver @@ -297,7 +297,7 @@ github.com/openshift/client-go/route/applyconfigurations/route/v1 github.com/openshift/client-go/route/clientset/versioned github.com/openshift/client-go/route/clientset/versioned/scheme github.com/openshift/client-go/route/clientset/versioned/typed/route/v1 -# github.com/openshift/library-go v0.0.0-20230501134757-c3f6a3f540c5 +# github.com/openshift/library-go v0.0.0-20230502160234-500059c0376f ## explicit; go 1.19 github.com/openshift/library-go/pkg/assets github.com/openshift/library-go/pkg/authorization/hardcodedauthorizer