Skip to content

Commit c639a31

Browse files
Merge pull request #2287 from jhadvig/include_base_image
OTA-1956: oc adm release new: Include base image's image-references in pruning
2 parents 40ce70f + 6af7486 commit c639a31

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

pkg/cli/admin/release/new.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"path"
1616
"path/filepath"
1717
"regexp"
18+
"slices"
1819
"sort"
1920
"strings"
2021
"sync"
@@ -711,6 +712,12 @@ func (o *NewOptions) Run(ctx context.Context) error {
711712
}
712713
}
713714
ordered = filteredNames
715+
716+
if len(o.ToImageBaseTag) > 0 {
717+
ordered = slices.DeleteFunc(ordered, func(s string) bool {
718+
return s == o.ToImageBaseTag
719+
})
720+
}
714721
}
715722

716723
if len(o.Mirror) > 0 {
@@ -1035,6 +1042,13 @@ func (o *NewOptions) extractManifests(is *imageapi.ImageStream, name string, met
10351042
}
10361043

10371044
if len(labels[annotationReleaseOperator]) == 0 {
1045+
if tag.Name == o.ToImageBaseTag {
1046+
if err := os.MkdirAll(dstDir, 0777); err != nil {
1047+
return false, err
1048+
}
1049+
klog.V(2).Infof("Image %s is the release base image, extracting for image-references", m.ImageRef)
1050+
return true, nil
1051+
}
10381052
klog.V(2).Infof("Image %s has no %s label, skipping", m.ImageRef, annotationReleaseOperator)
10391053
return false, nil
10401054
}

pkg/cli/admin/release/new_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package release
22

33
import (
4+
"bytes"
45
"context"
6+
"encoding/json"
7+
"os"
8+
"path/filepath"
9+
"slices"
510
"strings"
611
"testing"
712

@@ -12,6 +17,138 @@ import (
1217
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1318
)
1419

20+
// createImageStream builds an ImageStream with tags for the given names.
21+
func createImageStream(names ...string) *imageapi.ImageStream {
22+
is := &imageapi.ImageStream{
23+
TypeMeta: metav1.TypeMeta{
24+
Kind: "ImageStream",
25+
APIVersion: "image.openshift.io/v1",
26+
},
27+
}
28+
for _, name := range names {
29+
is.Spec.Tags = append(is.Spec.Tags, imageapi.TagReference{
30+
Name: name,
31+
From: &corev1.ObjectReference{
32+
Kind: "DockerImage",
33+
Name: "example.com/" + name + ":latest",
34+
},
35+
})
36+
}
37+
return is
38+
}
39+
40+
// writeImageReferences creates an image-references file in dir with the given tag names.
41+
func writeImageReferences(t *testing.T, dir string, names []string) {
42+
t.Helper()
43+
is := createImageStream(names...)
44+
data, err := json.Marshal(is)
45+
if err != nil {
46+
t.Fatalf("failed to marshal image-references: %v", err)
47+
}
48+
if err := os.WriteFile(filepath.Join(dir, "image-references"), data, 0644); err != nil {
49+
t.Fatalf("failed to write image-references: %v", err)
50+
}
51+
}
52+
53+
// tagNames returns the names of all tags in the image stream.
54+
func tagNames(is *imageapi.ImageStream) []string {
55+
var names []string
56+
for _, tag := range is.Spec.Tags {
57+
names = append(names, tag.Name)
58+
}
59+
return names
60+
}
61+
62+
func TestPruneUnreferencedImageStreams(t *testing.T) {
63+
64+
t.Run("images referenced by operator image-references are kept", func(t *testing.T) {
65+
dir := t.TempDir()
66+
operatorDir := filepath.Join(dir, "my-operator")
67+
if err := os.MkdirAll(operatorDir, 0777); err != nil {
68+
t.Fatal(err)
69+
}
70+
writeImageReferences(t, operatorDir, []string{"helper-image"})
71+
72+
is := createImageStream("my-operator", "helper-image", "unreferenced-image")
73+
metadata := map[string]imageData{
74+
"my-operator": {Directory: operatorDir},
75+
}
76+
77+
if err := pruneUnreferencedImageStreams(&bytes.Buffer{}, is, metadata, []string{"my-operator"}); err != nil {
78+
t.Fatal(err)
79+
}
80+
81+
names := tagNames(is)
82+
if !slices.Contains(names, "my-operator") {
83+
t.Error("expected my-operator to be kept (in include list)")
84+
}
85+
if !slices.Contains(names, "helper-image") {
86+
t.Error("expected helper-image to be kept (referenced by operator image-references)")
87+
}
88+
if slices.Contains(names, "unreferenced-image") {
89+
t.Error("expected unreferenced-image to be pruned")
90+
}
91+
})
92+
93+
t.Run("base image image-references prevents pruning", func(t *testing.T) {
94+
dir := t.TempDir()
95+
96+
operatorDir := filepath.Join(dir, "my-operator")
97+
if err := os.MkdirAll(operatorDir, 0777); err != nil {
98+
t.Fatal(err)
99+
}
100+
writeImageReferences(t, operatorDir, []string{"operator-dep"})
101+
102+
baseDir := filepath.Join(dir, "cluster-version-operator")
103+
if err := os.MkdirAll(baseDir, 0777); err != nil {
104+
t.Fatal(err)
105+
}
106+
writeImageReferences(t, baseDir, []string{"cluster-update-console-plugin"})
107+
108+
is := createImageStream("cluster-version-operator", "my-operator", "operator-dep", "cluster-update-console-plugin")
109+
metadata := map[string]imageData{
110+
"my-operator": {Directory: operatorDir},
111+
"cluster-version-operator": {Directory: baseDir},
112+
}
113+
114+
if err := pruneUnreferencedImageStreams(&bytes.Buffer{}, is, metadata, []string{"cluster-version-operator", "my-operator"}); err != nil {
115+
t.Fatal(err)
116+
}
117+
118+
names := tagNames(is)
119+
if !slices.Contains(names, "cluster-update-console-plugin") {
120+
t.Error("expected cluster-update-console-plugin to be kept (referenced by base image image-references)")
121+
}
122+
if !slices.Contains(names, "operator-dep") {
123+
t.Error("expected operator-dep to be kept (referenced by operator image-references)")
124+
}
125+
})
126+
127+
t.Run("without base image image-references the image is pruned", func(t *testing.T) {
128+
dir := t.TempDir()
129+
130+
operatorDir := filepath.Join(dir, "my-operator")
131+
if err := os.MkdirAll(operatorDir, 0777); err != nil {
132+
t.Fatal(err)
133+
}
134+
writeImageReferences(t, operatorDir, []string{"operator-dep"})
135+
136+
is := createImageStream("cluster-version-operator", "my-operator", "operator-dep", "cluster-update-console-plugin")
137+
metadata := map[string]imageData{
138+
"my-operator": {Directory: operatorDir},
139+
}
140+
141+
if err := pruneUnreferencedImageStreams(&bytes.Buffer{}, is, metadata, []string{"cluster-version-operator", "my-operator"}); err != nil {
142+
t.Fatal(err)
143+
}
144+
145+
names := tagNames(is)
146+
if slices.Contains(names, "cluster-update-console-plugin") {
147+
t.Error("expected cluster-update-console-plugin to be pruned (not referenced by any image-references)")
148+
}
149+
})
150+
}
151+
15152
func TestMirrorImages(t *testing.T) {
16153
ctx := context.Background()
17154

0 commit comments

Comments
 (0)