@@ -12,7 +12,7 @@ import (
1212 configv1 "github.com/openshift/api/config/v1"
1313 "github.com/openshift/library-go/pkg/manifest"
1414 "github.com/pkg/errors"
15- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured "
15+ "k8s.io/apimachinery/pkg/runtime "
1616 "k8s.io/apimachinery/pkg/runtime/schema"
1717 utilerrors "k8s.io/apimachinery/pkg/util/errors"
1818 "k8s.io/apimachinery/pkg/util/sets"
@@ -35,33 +35,9 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
3535 }
3636 )
3737
38- var requiredFeatureSet * string
39- if featureGateManifestPath != "" {
40- manifests , err := manifest .ManifestsFromFiles ([]string {featureGateManifestPath })
41- if err != nil {
42- return fmt .Errorf ("loading FeatureGate manifest: %w" , err )
43- }
44- if len (manifests ) != 1 {
45- return fmt .Errorf ("FeatureGate manifest %s contains %d manifests, but expected only one" , featureGateManifestPath , len (manifests ))
46- }
47- featureGateManifest := manifests [0 ]
48- expectedGVK := schema.GroupVersionKind {Kind : "FeatureGate" , Version : configv1 .GroupVersion .Version , Group : config .GroupName }
49- if featureGateManifest .GVK != expectedGVK {
50- return fmt .Errorf ("FeatureGate manifest %s GroupVersionKind %v, but expected %v" , featureGateManifest .OriginalFilename , featureGateManifest .GVK , expectedGVK )
51- }
52- featureSet , found , err := unstructured .NestedString (featureGateManifest .Obj .Object , "spec" , "featureSet" )
53- if err != nil {
54- return fmt .Errorf ("%s spec.featureSet lookup was not a string: %w" , featureGateManifest .String (), err )
55- } else if found {
56- requiredFeatureSet = & featureSet
57- klog .Infof ("--feature-gate-manifest-path=%s results in feature set %q" , featureGateManifest .OriginalFilename , featureSet )
58- } else {
59- requiredFeatureSet = ptr.To [string ]("" )
60- klog .Infof ("--feature-gate-manifest-path=%s does not set spec.featureSet, using the default feature set" , featureGateManifest .OriginalFilename )
61- }
62- } else {
63- requiredFeatureSet = ptr.To [string ]("" )
64- klog .Info ("--feature-gate-manifest-path is unset, using the default feature set" )
38+ requiredFeatureSet , enabledFeatureGates , err := parseFeatureGateManifest (featureGateManifestPath )
39+ if err != nil {
40+ return fmt .Errorf ("error parsing feature gate manifest: %w" , err )
6541 }
6642
6743 tasks := []struct {
@@ -94,7 +70,7 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
9470 }}
9571 var errs []error
9672 for _ , task := range tasks {
97- if err := renderDir (renderConfig , task .idir , task .odir , requiredFeatureSet , & clusterProfile , task .processTemplate , task .skipFiles , task .filterGroupKind ); err != nil {
73+ if err := renderDir (renderConfig , task .idir , task .odir , requiredFeatureSet , enabledFeatureGates , & clusterProfile , task .processTemplate , task .skipFiles , task .filterGroupKind ); err != nil {
9874 errs = append (errs , err )
9975 }
10076 }
@@ -106,7 +82,9 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
10682 return nil
10783}
10884
109- func renderDir (renderConfig manifestRenderConfig , idir , odir string , requiredFeatureSet * string , clusterProfile * string , processTemplate bool , skipFiles sets.Set [string ], filterGroupKind sets.Set [schema.GroupKind ]) error {
85+ func renderDir (renderConfig manifestRenderConfig , idir , odir string , requiredFeatureSet * string , enabledFeatureGates sets.Set [string ], clusterProfile * string , processTemplate bool , skipFiles sets.Set [string ], filterGroupKind sets.Set [schema.GroupKind ]) error {
86+ klog .Infof ("Filtering manifests in %s for feature set %v, cluster profile %v and enabled feature gates %v" , idir , * requiredFeatureSet , * clusterProfile , enabledFeatureGates .UnsortedList ())
87+
11088 if err := os .MkdirAll (odir , 0666 ); err != nil {
11189 return err
11290 }
@@ -126,16 +104,6 @@ func renderDir(renderConfig manifestRenderConfig, idir, odir string, requiredFea
126104 if skipFiles .Has (file .Name ()) {
127105 continue
128106 }
129- if strings .Contains (file .Name (), "CustomNoUpgrade" ) ||
130- strings .Contains (file .Name (), "TechPreviewNoUpgrade" ) ||
131- strings .Contains (file .Name (), "DevPreviewNoUpgrade" ) ||
132- strings .Contains (file .Name (), "OKD" ) {
133- // CustomNoUpgrade, TechPreviewNoUpgrade, DevPreviewNoUpgrade, and OKD may add features to manifests like the ClusterVersion CRD,
134- // but we do not need those features during bootstrap-render time. In those clusters, the production
135- // CVO will be along shortly to update the manifests and deliver the gated features.
136- // fixme: now that we have requiredFeatureSet, use it to do Manifest.Include() filtering here instead of making filename assumptions
137- continue
138- }
139107
140108 ipath := filepath .Join (idir , file .Name ())
141109 iraw , err := os .ReadFile (ipath )
@@ -155,34 +123,32 @@ func renderDir(renderConfig manifestRenderConfig, idir, odir string, requiredFea
155123 rraw = iraw
156124 }
157125
158- if len (filterGroupKind ) > 0 {
159- manifests , err := manifest .ParseManifests (bytes .NewReader (rraw ))
160- if err != nil {
161- errs = append (errs , fmt .Errorf ("parse manifest %s from %s: %w" , file .Name (), idir , err ))
162- continue
163- }
126+ manifests , err := manifest .ParseManifests (bytes .NewReader (rraw ))
127+ if err != nil {
128+ errs = append (errs , fmt .Errorf ("parse manifest %s from %s: %w" , file .Name (), idir , err ))
129+ continue
130+ }
164131
165- filteredManifests := make ([]string , 0 , len (manifests ))
166- for _ , manifest := range manifests {
167- if ! filterGroupKind .Has (manifest .GVK .GroupKind ()) {
168- klog .Infof ("excluding %s because we do not render that group/kind" , manifest .String ())
169- } else if err := manifest .Include (nil , requiredFeatureSet , clusterProfile , nil , nil , sets.Set [string ]{} /* Empty, assuming any gated manifest will be applied later by the real CVO */ ); err != nil {
170- klog .Infof ("excluding %s: %v" , manifest .String (), err )
171- } else {
172- filteredManifests = append (filteredManifests , string (manifest .Raw ))
173- klog .Infof ("including %s filtered by feature set %v and cluster profile %v" , manifest .String (), requiredFeatureSet , clusterProfile )
174- }
132+ filteredManifests := make ([]string , 0 , len (manifests ))
133+ for _ , manifest := range manifests {
134+ if len (filterGroupKind ) > 0 && ! filterGroupKind .Has (manifest .GVK .GroupKind ()) {
135+ klog .Infof ("excluding %s because we do not render that group/kind" , manifest .String ())
136+ } else if err := manifest .Include (nil , requiredFeatureSet , clusterProfile , nil , nil , enabledFeatureGates ); err != nil {
137+ klog .Infof ("excluding %s: %v" , manifest .String (), err )
138+ } else {
139+ filteredManifests = append (filteredManifests , string (manifest .Raw ))
140+ klog .Infof ("including %s" , manifest .String ())
175141 }
142+ }
176143
177- if len (filteredManifests ) == 0 {
178- continue
179- }
144+ if len (filteredManifests ) == 0 {
145+ continue
146+ }
180147
181- if len (filteredManifests ) == 1 {
182- rraw = []byte (filteredManifests [0 ])
183- } else {
184- rraw = []byte (strings .Join (filteredManifests , "\n ---\n " ))
185- }
148+ if len (filteredManifests ) == 1 {
149+ rraw = []byte (filteredManifests [0 ])
150+ } else {
151+ rraw = []byte (strings .Join (filteredManifests , "\n ---\n " ))
186152 }
187153
188154 opath := filepath .Join (odir , file .Name ())
@@ -218,3 +184,57 @@ func renderManifest(config manifestRenderConfig, manifestBytes []byte) ([]byte,
218184
219185 return buf .Bytes (), nil
220186}
187+
188+ func parseFeatureGateManifest (featureGateManifestPath string ) (* string , sets.Set [string ], error ) {
189+ if featureGateManifestPath == "" {
190+ return ptr .To ("" ), sets.Set [string ]{}, nil
191+ }
192+
193+ manifests , err := manifest .ManifestsFromFiles ([]string {featureGateManifestPath })
194+ if err != nil {
195+ return nil , nil , fmt .Errorf ("loading FeatureGate manifest: %w" , err )
196+ }
197+
198+ if len (manifests ) != 1 {
199+ return nil , nil , fmt .Errorf ("FeatureGate manifest %s contains %d manifests, but expected only one" , featureGateManifestPath , len (manifests ))
200+ }
201+
202+ featureGateManifest := manifests [0 ]
203+ expectedGVK := schema.GroupVersionKind {Kind : "FeatureGate" , Version : configv1 .GroupVersion .Version , Group : config .GroupName }
204+ if featureGateManifest .GVK != expectedGVK {
205+ return nil , nil , fmt .Errorf ("FeatureGate manifest %s GroupVersionKind %v, but expected %v" , featureGateManifest .OriginalFilename , featureGateManifest .GVK , expectedGVK )
206+ }
207+
208+ // Convert unstructured object to structured FeatureGate
209+ var featureGate configv1.FeatureGate
210+ if err := runtime .DefaultUnstructuredConverter .FromUnstructured (featureGateManifest .Obj .Object , & featureGate ); err != nil {
211+ return nil , nil , fmt .Errorf ("failed to convert FeatureGate manifest %s to structured object: %w" , featureGateManifest .OriginalFilename , err )
212+ }
213+
214+ var requiredFeatureSet * string
215+ if featureGate .Spec .FeatureSet != "" {
216+ featureSetString := string (featureGate .Spec .FeatureSet )
217+ requiredFeatureSet = & featureSetString
218+ klog .Infof ("--feature-gate-manifest-path=%s results in feature set %q" , featureGateManifest .OriginalFilename , featureGate .Spec .FeatureSet )
219+ } else {
220+ requiredFeatureSet = ptr .To ("" )
221+ klog .Infof ("--feature-gate-manifest-path=%s does not set spec.featureSet, using the default feature set" , featureGateManifest .OriginalFilename )
222+ }
223+
224+ if len (featureGate .Status .FeatureGates ) == 0 {
225+ // In case there are no feature gates, fall back to feature set only behaviour.
226+ return requiredFeatureSet , sets.Set [string ]{}, nil
227+ }
228+
229+ // A rendered manifest should only include 1 version of the enabled feature gates.
230+ if len (featureGate .Status .FeatureGates ) > 1 {
231+ return nil , nil , fmt .Errorf ("FeatureGate manifest %s contains %d feature gates, but expected exactly one" , featureGateManifest .OriginalFilename , len (featureGate .Status .FeatureGates ))
232+ }
233+
234+ enabledFeatureGates := sets.Set [string ]{}
235+ for _ , feature := range featureGate .Status .FeatureGates [0 ].Enabled {
236+ enabledFeatureGates .Insert (string (feature .Name ))
237+ }
238+
239+ return requiredFeatureSet , enabledFeatureGates , nil
240+ }
0 commit comments