Skip to content

Commit 12e4168

Browse files
Merge pull request #1410 from jhadvig/cvo-images-template-support
OTA-1956: pkg/payload: Add Images map and tolerate unknown template fields during upgrades
2 parents 415acd5 + 69db689 commit 12e4168

8 files changed

Lines changed: 148 additions & 3 deletions

File tree

pkg/payload/payload.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func LoadUpdate(dir, releaseImage, excludeIdentifier string, requiredFeatureSet
153153
return nil, err
154154
}
155155

156-
tasks := loadPayloadTasks(releaseDir, cvoDir, releaseImage, profile)
156+
tasks := loadPayloadTasks(releaseDir, cvoDir, releaseImage, profile, payload.ImageRef)
157157

158158
var onlyKnownCaps *configv1.ClusterVersionCapabilitiesStatus
159159

@@ -195,7 +195,13 @@ func LoadUpdate(dir, releaseImage, excludeIdentifier string, requiredFeatureSet
195195
if task.preprocess != nil {
196196
raw, err = task.preprocess(raw)
197197
if err != nil {
198-
errs = append(errs, fmt.Errorf("preprocess %s: %w", file.Name(), err))
198+
// Template rendering may fail when an older CVO binary
199+
// loads a newer payload that uses template fields the
200+
// older binary does not know about (e.g. .Images). Skip
201+
// the manifest with a warning — the new CVO binary will
202+
// re-load the full payload after it replaces the old one
203+
// at run-level 0.
204+
klog.Warningf("Skipping manifest %s: template rendering failed (may require newer CVO): %v", file.Name(), err)
199205
continue
200206
}
201207
}
@@ -317,13 +323,14 @@ type payloadTasks struct {
317323
skipFiles sets.Set[string]
318324
}
319325

320-
func loadPayloadTasks(releaseDir, cvoDir, releaseImage, clusterProfile string) []payloadTasks {
326+
func loadPayloadTasks(releaseDir, cvoDir, releaseImage, clusterProfile string, imageRef *imagev1.ImageStream) []payloadTasks {
321327
cjf := filepath.Join(releaseDir, cincinnatiJSONFile)
322328
irf := filepath.Join(releaseDir, imageReferencesFile)
323329

324330
mrc := manifestRenderConfig{
325331
ReleaseImage: releaseImage,
326332
ClusterProfile: clusterProfile,
333+
Images: imagesFromImageRef(imageRef),
327334
}
328335

329336
return []payloadTasks{{

pkg/payload/payload_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,28 @@ func TestLoadUpdate(t *testing.T) {
150150
}
151151
}
152152

153+
func TestLoadUpdateSkipsUnknownTemplateFields(t *testing.T) {
154+
update, err := LoadUpdate("testdata/payload-unknown-template", "image:test", "", "", DefaultClusterProfile, nil, sets.Set[string]{})
155+
if err != nil {
156+
t.Fatalf("LoadUpdate should not fail when a manifest uses unknown template fields, got: %v", err)
157+
}
158+
159+
// The valid manifest (using known .ReleaseImage) should be loaded
160+
var foundValid bool
161+
for _, m := range update.Manifests {
162+
if m.OriginalFilename == "0000_00_valid.yaml" {
163+
foundValid = true
164+
}
165+
// The manifest with unknown .FutureField should be skipped (not loaded)
166+
if m.OriginalFilename == "0000_50_unknown-field.yaml" {
167+
t.Error("manifest with unknown template field should have been skipped, but was loaded")
168+
}
169+
}
170+
if !foundValid {
171+
t.Error("expected valid manifest (0000_00_valid.yaml) to be loaded")
172+
}
173+
}
174+
153175
func TestLoadUpdateArchitecture(t *testing.T) {
154176
type args struct {
155177
dir string

pkg/payload/render.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/openshift/api/config"
2222
configv1 "github.com/openshift/api/config/v1"
23+
imagev1 "github.com/openshift/api/image/v1"
2324
"github.com/openshift/library-go/pkg/manifest"
2425
)
2526

@@ -38,6 +39,12 @@ func Render(outputDir, releaseImage, clusterVersionManifestPath, featureGateMani
3839
}
3940
)
4041

42+
imageRef, err := loadImageReferences(releaseManifestsDir)
43+
if err != nil {
44+
return fmt.Errorf("error loading image references for manifest rendering: %w", err)
45+
}
46+
renderConfig.Images = imagesFromImageRef(imageRef)
47+
4148
overrides, err := parseClusterVersionManifest(clusterVersionManifestPath)
4249
if err != nil {
4350
return fmt.Errorf("error parsing cluster version manifest: %w", err)
@@ -181,6 +188,21 @@ func renderDir(renderConfig manifestRenderConfig, idir, odir string, overrides [
181188
type manifestRenderConfig struct {
182189
ReleaseImage string
183190
ClusterProfile string
191+
Images map[string]string
192+
}
193+
194+
// imagesFromImageRef builds a map from image short names to their resolved URIs.
195+
func imagesFromImageRef(imageRef *imagev1.ImageStream) map[string]string {
196+
images := make(map[string]string)
197+
if imageRef == nil {
198+
return images
199+
}
200+
for _, tag := range imageRef.Spec.Tags {
201+
if tag.From != nil && tag.From.Kind == "DockerImage" {
202+
images[tag.Name] = tag.From.Name
203+
}
204+
}
205+
return images
184206
}
185207

186208
// renderManifest Executes go text template from `manifestBytes` with `config`.

pkg/payload/render_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,82 @@ import (
1111

1212
"github.com/google/go-cmp/cmp"
1313

14+
corev1 "k8s.io/api/core/v1"
1415
"k8s.io/apimachinery/pkg/runtime/schema"
1516
"k8s.io/apimachinery/pkg/util/sets"
1617
"k8s.io/utils/ptr"
1718

1819
configv1 "github.com/openshift/api/config/v1"
20+
imagev1 "github.com/openshift/api/image/v1"
1921
"github.com/openshift/library-go/pkg/manifest"
2022
)
2123

24+
func TestImagesFromImageRef(t *testing.T) {
25+
tests := []struct {
26+
name string
27+
imageRef *imagev1.ImageStream
28+
expected map[string]string
29+
}{
30+
{
31+
name: "nil ImageStream returns empty map",
32+
imageRef: nil,
33+
expected: map[string]string{},
34+
},
35+
{
36+
name: "maps DockerImage tags to their names",
37+
imageRef: &imagev1.ImageStream{
38+
Spec: imagev1.ImageStreamSpec{
39+
Tags: []imagev1.TagReference{
40+
{
41+
Name: "console",
42+
From: &corev1.ObjectReference{Kind: "DockerImage", Name: "quay.io/openshift/console:latest"},
43+
},
44+
{
45+
Name: "cluster-update-console-plugin",
46+
From: &corev1.ObjectReference{Kind: "DockerImage", Name: "quay.io/openshift/plugin:v1"},
47+
},
48+
},
49+
},
50+
},
51+
expected: map[string]string{
52+
"console": "quay.io/openshift/console:latest",
53+
"cluster-update-console-plugin": "quay.io/openshift/plugin:v1",
54+
},
55+
},
56+
{
57+
name: "skips non-DockerImage tags",
58+
imageRef: &imagev1.ImageStream{
59+
Spec: imagev1.ImageStreamSpec{
60+
Tags: []imagev1.TagReference{
61+
{
62+
Name: "docker-tag",
63+
From: &corev1.ObjectReference{Kind: "DockerImage", Name: "example.com/img:v1"},
64+
},
65+
{
66+
Name: "image-stream-tag",
67+
From: &corev1.ObjectReference{Kind: "ImageStreamTag", Name: "other:latest"},
68+
},
69+
{
70+
Name: "no-from",
71+
},
72+
},
73+
},
74+
},
75+
expected: map[string]string{
76+
"docker-tag": "example.com/img:v1",
77+
},
78+
},
79+
}
80+
for _, tt := range tests {
81+
t.Run(tt.name, func(t *testing.T) {
82+
actual := imagesFromImageRef(tt.imageRef)
83+
if diff := cmp.Diff(tt.expected, actual); diff != "" {
84+
t.Errorf("imagesFromImageRef mismatch (-want +got):\n%s", diff)
85+
}
86+
})
87+
}
88+
}
89+
2290
func TestRenderManifest(t *testing.T) {
2391

2492
tests := []struct {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: valid-manifest
5+
namespace: test
6+
annotations:
7+
include.release.openshift.io/self-managed-high-availability: "true"
8+
data:
9+
release-image: '{{.ReleaseImage}}'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: future-manifest
5+
namespace: test
6+
annotations:
7+
include.release.openshift.io/self-managed-high-availability: "true"
8+
data:
9+
image: '{{index .FutureField "some-key"}}'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"kind": "ImageStream",
3+
"apiVersion": "image.openshift.io/v1",
4+
"metadata": {
5+
"name": "1.0.0-test"
6+
}
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"kind":"cincinnati-metadata-v0","version":"1.0.0-test"}

0 commit comments

Comments
 (0)