Skip to content

Commit a837056

Browse files
committed
cmd/cluster-version-operator/render: Add --cluster-version-manifest-path option
Like a3a6a16 (cmd/render: Add --feature-gate-manifest-path option, 2024-08-07, #1078), but for ClusterVersion spec.overrides. We need this in place to avoid bootstrapping failure when the CVO renders the ClusterImagePolicy despite OPENSHIFT_INSTALL_EXPERIMENTAL_DISABLE_IMAGE_POLICY having been set to trigger the installer to set an override waiving the ClusterImagePolicy [1]: $ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs/periodic-ci-openshift-release-master-nightly-4.22-e2e-aws-ovn-serial-1of2/2020129461281755136/artifacts/e2e-aws-ovn-serial/ipi-install-install/artifacts/log-bundle-20260207143404.tar | tar -tvz | grep 'cluster.*image.*polic' -rw-r--r-- core/core 1678 2026-02-07 06:34 log-bundle-20260207143404/rendered-assets/openshift/cvo-bootstrap/manifests/0000_90_openshift-cluster-image-policy.yaml -rw-r--r-- core/core 1678 2026-02-07 06:34 log-bundle-20260207143404/rendered-assets/openshift/manifests/0000_90_openshift-cluster-image-policy.yaml The rendered ClusterImagePolicy is consumed by the bootstrap machine-config operator, and it breaks the ability of unsigned nightly control-plane nodes to launch [1]: $ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs/periodic-ci-openshift-release-master-nightly-4.22-e2e-aws-ovn-serial-1of2/2020129461281755136/artifacts/e2e-aws-ovn-serial/ipi-install-install/artifacts/log-bundle-20260207143404.tar | tar -tvz | grep 'control-plane.*journal.log.gz' -rw-r--r-- core/core 98143 2026-02-07 06:34 log-bundle-20260207143404/control-plane/10.0.110.120/journals/journal.log.gz -rw-r--r-- core/core 101064 2026-02-07 06:34 log-bundle-20260207143404/control-plane/10.0.5.2/journals/journal.log.gz -rw-r--r-- core/core 96700 2026-02-07 06:34 log-bundle-20260207143404/control-plane/10.0.71.227/journals/journal.log.gz $ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs/periodic-ci-openshift-release-master-nightly-4.22-e2e-aws-ovn-serial-1of2/2020129461281755136/artifacts/e2e-aws-ovn-serial/ipi-install-install/artifacts/log-bundle-20260207143404.tar | tar -xOz log-bundle-20260207143404/control-plane/10.0.5.2/journals/journal.log.gz | zgrep 'signature was required' | head -n4 Sat 2026-02-07 13:51:08 UTC ip-10-0-5-2 machine-config-daemon-pull.service[2026]: Error: Source image rejected: A signature was required, but no signature exists Sat 2026-02-07 13:51:08 UTC ip-10-0-5-2 machine-config-daemon-pull.service[2026]: 2026-02-07 13:51:08.129594235 +0000 UTC m=+0.399222333 image pull-error quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:b4b3cc836d480ea7156ea797712e9af742fb73e5fb56a9cf9e63aeae11875315 Source image rejected: A signature was required, but no signature exists Sat 2026-02-07 13:51:09 UTC ip-10-0-5-2 machine-config-daemon-pull.service[2037]: Error: Source image rejected: A signature was required, but no signature exists Sat 2026-02-07 13:51:09 UTC ip-10-0-5-2 machine-config-daemon-pull.service[2037]: 2026-02-07 13:51:09.533298454 +0000 UTC m=+0.389622944 image pull-error quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:b4b3cc836d480ea7156ea797712e9af742fb73e5fb56a9cf9e63aeae11875315 Source image rejected: A signature was required, but no signature exists [1]: https://prow.ci.openshift.org/view/gs/test-platform-results/logs/periodic-ci-openshift-release-master-nightly-4.22-e2e-aws-ovn-serial-1of2/2020129461281755136
1 parent bdd3553 commit a837056

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

cmd/cluster-version-operator/render.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,18 @@ var (
1919
}
2020

2121
renderOpts struct {
22-
releaseImage string
23-
featureGateManifestPath string
24-
outputDir string
22+
releaseImage string
23+
clusterVersionManifestPath string
24+
featureGateManifestPath string
25+
outputDir string
2526
}
2627
)
2728

2829
func init() {
2930
rootCmd.AddCommand(renderCmd)
3031
renderCmd.PersistentFlags().StringVar(&renderOpts.outputDir, "output-dir", "", "The output directory where the manifests will be rendered.")
3132
renderCmd.PersistentFlags().StringVar(&renderOpts.releaseImage, "release-image", "", "The Openshift release image url.")
33+
renderCmd.PersistentFlags().StringVar(&renderOpts.clusterVersionManifestPath, "cluster-version-manifest-path", "", "ClusterVersion manifest input path.")
3234
renderCmd.PersistentFlags().StringVar(&renderOpts.featureGateManifestPath, "feature-gate-manifest-path", "", "FeatureGate manifest input path.")
3335
}
3436

@@ -42,7 +44,7 @@ func runRenderCmd(cmd *cobra.Command, args []string) {
4244
if renderOpts.releaseImage == "" {
4345
klog.Fatalf("missing --release-image flag, it is required")
4446
}
45-
if err := payload.Render(renderOpts.outputDir, renderOpts.releaseImage, renderOpts.featureGateManifestPath, clusterProfile()); err != nil {
47+
if err := payload.Render(renderOpts.outputDir, renderOpts.releaseImage, renderOpts.clusterVersionManifestPath, renderOpts.featureGateManifestPath, clusterProfile()); err != nil {
4648
klog.Fatalf("Render command failed: %v", err)
4749
}
4850
}

pkg/payload/render.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
)
2222

2323
// Render renders critical manifests from /manifests to outputDir.
24-
func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile string) error {
24+
func Render(outputDir, releaseImage, clusterVersionManifestPath, featureGateManifestPath, clusterProfile string) error {
2525
var (
2626
manifestsDir = filepath.Join(DefaultPayloadDir, CVOManifestDir)
2727
releaseManifestsDir = filepath.Join(DefaultPayloadDir, ReleaseManifestDir)
@@ -35,6 +35,11 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
3535
}
3636
)
3737

38+
overrides, err := parseClusterVersionManifest(clusterVersionManifestPath)
39+
if err != nil {
40+
return fmt.Errorf("error parsing cluster version manifest: %w", err)
41+
}
42+
3843
requiredFeatureSet, enabledFeatureGates, err := parseFeatureGateManifest(featureGateManifestPath)
3944
if err != nil {
4045
return fmt.Errorf("error parsing feature gate manifest: %w", err)
@@ -70,7 +75,7 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
7075
}}
7176
var errs []error
7277
for _, task := range tasks {
73-
if err := renderDir(renderConfig, task.idir, task.odir, requiredFeatureSet, enabledFeatureGates, &clusterProfile, task.processTemplate, task.skipFiles, task.filterGroupKind); err != nil {
78+
if err := renderDir(renderConfig, task.idir, task.odir, overrides, requiredFeatureSet, enabledFeatureGates, &clusterProfile, task.processTemplate, task.skipFiles, task.filterGroupKind); err != nil {
7479
errs = append(errs, err)
7580
}
7681
}
@@ -82,7 +87,7 @@ func Render(outputDir, releaseImage, featureGateManifestPath, clusterProfile str
8287
return nil
8388
}
8489

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 {
90+
func renderDir(renderConfig manifestRenderConfig, idir, odir string, overrides []configv1.ComponentOverride, requiredFeatureSet *string, enabledFeatureGates sets.Set[string], clusterProfile *string, processTemplate bool, skipFiles sets.Set[string], filterGroupKind sets.Set[schema.GroupKind]) error {
8691
klog.Infof("Filtering manifests in %s for feature set %v, cluster profile %v and enabled feature gates %v", idir, *requiredFeatureSet, *clusterProfile, enabledFeatureGates.UnsortedList())
8792

8893
if err := os.MkdirAll(odir, 0666); err != nil {
@@ -133,7 +138,7 @@ func renderDir(renderConfig manifestRenderConfig, idir, odir string, requiredFea
133138
for _, manifest := range manifests {
134139
if len(filterGroupKind) > 0 && !filterGroupKind.Has(manifest.GVK.GroupKind()) {
135140
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 {
141+
} else if err := manifest.Include(nil, requiredFeatureSet, clusterProfile, nil, overrides, enabledFeatureGates); err != nil {
137142
klog.Infof("excluding %s: %v", manifest.String(), err)
138143
} else {
139144
filteredManifests = append(filteredManifests, string(manifest.Raw))
@@ -185,6 +190,35 @@ func renderManifest(config manifestRenderConfig, manifestBytes []byte) ([]byte,
185190
return buf.Bytes(), nil
186191
}
187192

193+
func parseClusterVersionManifest(clusterVersionManifestPath string) ([]configv1.ComponentOverride, error) {
194+
if clusterVersionManifestPath == "" {
195+
return nil, nil
196+
}
197+
198+
manifests, err := manifest.ManifestsFromFiles([]string{clusterVersionManifestPath})
199+
if err != nil {
200+
return nil, fmt.Errorf("loading ClusterVersion manifest: %w", err)
201+
}
202+
203+
if len(manifests) != 1 {
204+
return nil, fmt.Errorf("ClusterVersion manifest %s contains %d manifests, but expected only one", clusterVersionManifestPath, len(manifests))
205+
}
206+
207+
clusterVersionManifest := manifests[0]
208+
expectedGVK := schema.GroupVersionKind{Kind: "ClusterVersion", Version: configv1.GroupVersion.Version, Group: config.GroupName}
209+
if clusterVersionManifest.GVK != expectedGVK {
210+
return nil, fmt.Errorf("ClusterVersion manifest %s GroupVersionKind %v, but expected %v", clusterVersionManifest.OriginalFilename, clusterVersionManifest.GVK, expectedGVK)
211+
}
212+
213+
// Convert unstructured object to structured ClusterVersion
214+
var clusterVersion configv1.ClusterVersion
215+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(clusterVersionManifest.Obj.Object, &clusterVersion); err != nil {
216+
return nil, fmt.Errorf("failed to convert ClusterVersion manifest %s to structured object: %w", clusterVersionManifest.OriginalFilename, err)
217+
}
218+
219+
return clusterVersion.Spec.Overrides, nil
220+
}
221+
188222
func parseFeatureGateManifest(featureGateManifestPath string) (*string, sets.Set[string], error) {
189223
if featureGateManifestPath == "" {
190224
return ptr.To(""), sets.Set[string]{}, nil

0 commit comments

Comments
 (0)