Skip to content

Commit f99cb22

Browse files
committed
UPSTREAM: <drop>: patch to use mapped images
1 parent 38edf68 commit f99cb22

File tree

3 files changed

+1237
-0
lines changed

3 files changed

+1237
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
package images
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"sort"
8+
"strings"
9+
"time"
10+
11+
"golang.org/x/exp/slices"
12+
13+
"github.com/openshift-eng/openshift-tests-extension/pkg/extension"
14+
"github.com/openshift/library-go/pkg/image/reference"
15+
"github.com/openshift/origin/pkg/clioptions/imagesetup"
16+
"github.com/openshift/origin/pkg/cmd"
17+
"github.com/openshift/origin/pkg/test/extensions"
18+
"github.com/openshift/origin/test/extended/util/image"
19+
"github.com/spf13/cobra"
20+
"k8s.io/kube-openapi/pkg/util/sets"
21+
"k8s.io/kubectl/pkg/util/templates"
22+
k8simage "k8s.io/kubernetes/test/utils/image"
23+
)
24+
25+
func NewImagesCommand() *cobra.Command {
26+
o := &imagesOptions{}
27+
cmd := &cobra.Command{
28+
Use: "images",
29+
Short: "Gather images required for testing",
30+
Long: templates.LongDesc(fmt.Sprintf(`
31+
Creates a mapping to mirror test images to a private registry
32+
33+
This command identifies the locations of all test images referenced by the test
34+
suite and outputs a mirror list for use with 'oc image mirror' to copy those images
35+
to a private registry. The list may be passed via file or standard input.
36+
37+
$ openshift-tests images --to-repository private.com/test/repository > /tmp/mirror
38+
$ oc image mirror -f /tmp/mirror
39+
40+
The 'run' and 'run-upgrade' subcommands accept '--from-repository' which will source
41+
required test images from your mirror.
42+
43+
See the help for 'oc image mirror' for more about mirroring to disk or consult the docs
44+
for mirroring offline. You may use a file:// prefix in your '--to-repository', but when
45+
mirroring from disk to your offline repository you will have to construct the appropriate
46+
disk to internal registry statements yourself.
47+
48+
By default, the test images are sourced from a public container image repository at
49+
%[1]s and are provided as-is for testing purposes only. Images are mirrored by the project
50+
to the public repository periodically.
51+
`, imagesetup.DefaultTestImageMirrorLocation)),
52+
PersistentPreRun: cmd.NoPrintVersion,
53+
SilenceUsage: true,
54+
SilenceErrors: true,
55+
RunE: func(cmd *cobra.Command, args []string) error {
56+
if err := imagesetup.VerifyTestImageRepoEnvVarUnset(); err != nil {
57+
return err
58+
}
59+
60+
if o.Verify {
61+
return imagesetup.VerifyImages()
62+
}
63+
64+
repository := o.Repository
65+
var prefix string
66+
for _, validPrefix := range []string{"file://", "s3://"} {
67+
if strings.HasPrefix(repository, validPrefix) {
68+
repository = strings.TrimPrefix(repository, validPrefix)
69+
prefix = validPrefix
70+
break
71+
}
72+
}
73+
ref, err := reference.Parse(repository)
74+
if err != nil {
75+
return fmt.Errorf("--to-repository is not valid: %v", err)
76+
}
77+
if len(ref.Tag) > 0 || len(ref.ID) > 0 {
78+
return fmt.Errorf("--to-repository may not include a tag or image digest")
79+
}
80+
81+
if err := imagesetup.VerifyImages(); err != nil {
82+
return err
83+
}
84+
lines, err := createImageMirrorForInternalImages(prefix, ref, !o.Upstream)
85+
if err != nil {
86+
return err
87+
}
88+
for _, line := range lines {
89+
fmt.Fprintln(os.Stdout, line)
90+
}
91+
return nil
92+
},
93+
}
94+
cmd.Flags().BoolVar(&o.Upstream, "upstream", o.Upstream, "Retrieve images from the default upstream location")
95+
cmd.Flags().StringVar(&o.Repository, "to-repository", o.Repository, "A container image repository to mirror to.")
96+
// this is a private flag for debugging only
97+
cmd.Flags().BoolVar(&o.Verify, "verify", o.Verify, "Verify the contents of the image mappings")
98+
cmd.Flags().MarkHidden("verify")
99+
return cmd
100+
}
101+
102+
type imagesOptions struct {
103+
Repository string
104+
Upstream bool
105+
Verify bool
106+
}
107+
108+
// createImageMirrorForInternalImages returns a list of 'oc image mirror' mappings from source to
109+
// target or returns an error. If mirrored is true the images are assumed to have already been copied
110+
// from their upstream location into our official mirror, in the REPO:TAG format where TAG is a hash
111+
// of the original internal name and the index of the image in the array. Otherwise the mappings will
112+
// be set to mirror the location as defined in the test code into our official mirror, where the target
113+
// TAG is the hash described above.
114+
func createImageMirrorForInternalImages(prefix string, ref reference.DockerImageReference, mirrored bool) ([]string, error) {
115+
source := ref.Exact()
116+
externalImageSets := []extension.Image{}
117+
initialImageSets := []extensions.ImageSet{
118+
k8simage.GetOriginalImageConfigs(),
119+
}
120+
121+
// If ENV is not set, the list of images should come from external binaries
122+
if len(os.Getenv("OPENSHIFT_SKIP_EXTERNAL_TESTS")) == 0 {
123+
extractionContext, extractionContextCancel := context.WithTimeout(context.Background(), 30*time.Minute)
124+
defer extractionContextCancel()
125+
cleanUpFn, externalBinaries, err := extensions.ExtractAllTestBinaries(extractionContext, 10)
126+
if err != nil {
127+
return nil, err
128+
}
129+
defer cleanUpFn()
130+
131+
// List test images from all available binaries
132+
listContext, listContextCancel := context.WithTimeout(context.Background(), time.Minute)
133+
defer listContextCancel()
134+
imageSetsFromBinaries, err := externalBinaries.ListImages(listContext, 10)
135+
if err != nil {
136+
return nil, err
137+
}
138+
if len(imageSetsFromBinaries) == 0 {
139+
return nil, fmt.Errorf("no test images were reported by external binaries")
140+
}
141+
externalImageSets = imageSetsFromBinaries
142+
}
143+
144+
// Convert external images to initial and updated image sets
145+
// Add mapped images to updated image set if they exist
146+
exceptions := image.Exceptions.List()
147+
updatedImageSets := []extensions.ImageSet{}
148+
initial := extensions.ImageSet{}
149+
updated := extensions.ImageSet{}
150+
for _, image := range externalImageSets {
151+
imageConfig := covertMappedImageToImageConfig(image)
152+
if !slices.ContainsFunc(exceptions, func(e string) bool {
153+
return strings.Contains(imageConfig.GetE2EImage(), e)
154+
}) {
155+
initial[k8simage.ImageID(image.Index)] = imageConfig
156+
if image.Mapped != nil {
157+
updated[k8simage.ImageID(image.Index)] = covertMappedImageToImageConfig(*image.Mapped)
158+
}
159+
}
160+
}
161+
if len(initial) > 0 {
162+
initialImageSets = []extensions.ImageSet{initial}
163+
}
164+
165+
// Take the initial images coming from external binaries and remove any exceptions that might exist.
166+
defaultImageSets := []extensions.ImageSet{}
167+
for i := range initialImageSets {
168+
filtered := extensions.ImageSet{}
169+
for imageID, imageConfig := range initialImageSets[i] {
170+
if !slices.ContainsFunc(exceptions, func(e string) bool {
171+
return strings.Contains(imageConfig.GetE2EImage(), e)
172+
}) {
173+
filtered[imageID] = imageConfig
174+
}
175+
}
176+
if len(filtered) > 0 {
177+
defaultImageSets = append(defaultImageSets, filtered)
178+
}
179+
}
180+
181+
// Map initial images to the target repository
182+
for _, img := range defaultImageSets {
183+
for imageID, imageConfig := range img {
184+
// If the imageID is in the updated image set, skip it
185+
if _, ok := updated[imageID]; ok {
186+
continue
187+
}
188+
m := map[string]k8simage.ImageID{
189+
imageConfig.GetE2EImage(): k8simage.ImageID(imageID),
190+
}
191+
mappedImage := map[string]string{}
192+
switch imageID {
193+
// These images are special and can't be run out of the cloud - some because they
194+
// are authenticated, and others because they are not real images. Tests that depend
195+
// on these images can't be run without access to the public internet.
196+
case k8simage.InvalidRegistryImage, k8simage.AgnhostPrivate, k8simage.AuthenticatedAlpine:
197+
mappedImage[imageConfig.GetE2EImage()] = imageConfig.GetE2EImage()
198+
default:
199+
mappedImage = image.GetMappedImages(m, source)
200+
}
201+
ref, err := reference.Parse(mappedImage[imageConfig.GetE2EImage()])
202+
if err != nil {
203+
continue
204+
}
205+
config := k8simage.Config{}
206+
config.SetRegistry(ref.Registry)
207+
config.SetName(ref.RepositoryName())
208+
config.SetVersion(ref.Tag)
209+
updated[k8simage.ImageID(imageID)] = config
210+
}
211+
}
212+
213+
updatedImageSets = []extensions.ImageSet{updated}
214+
215+
openshiftDefaults := image.OriginalImages()
216+
openshiftUpdated := image.GetMappedImages(openshiftDefaults, imagesetup.DefaultTestImageMirrorLocation)
217+
218+
// if we've mirrored, then the source is going to be our repo, not upstream's
219+
if mirrored {
220+
baseRef, err := reference.Parse(imagesetup.DefaultTestImageMirrorLocation)
221+
if err != nil {
222+
return nil, fmt.Errorf("invalid default mirror location: %v", err)
223+
}
224+
225+
// calculate the mapping of upstream images by setting defaults to baseRef
226+
covered := sets.NewString()
227+
for i := range updatedImageSets {
228+
for imageID, imageConfig := range updatedImageSets[i] {
229+
originalConfig := defaultImageSets[i][imageID]
230+
pullSpec := imageConfig.GetE2EImage()
231+
if pullSpec == originalConfig.GetE2EImage() {
232+
continue
233+
}
234+
if covered.Has(pullSpec) {
235+
continue
236+
}
237+
covered.Insert(pullSpec)
238+
e2eRef, err := reference.Parse(pullSpec)
239+
if err != nil {
240+
return nil, fmt.Errorf("invalid test image: %s: %v", pullSpec, err)
241+
}
242+
if len(e2eRef.Tag) == 0 {
243+
return nil, fmt.Errorf("invalid test image: %s: no tag", pullSpec)
244+
}
245+
imageConfig.SetRegistry(baseRef.Registry)
246+
imageConfig.SetName(baseRef.RepositoryName())
247+
imageConfig.SetVersion(e2eRef.Tag)
248+
defaultImageSets[i][imageID] = imageConfig
249+
}
250+
}
251+
252+
// calculate the mapping for openshift images by populating openshiftUpdated
253+
openshiftUpdated = make(map[string]string)
254+
sourceMappings := image.GetMappedImages(openshiftDefaults, imagesetup.DefaultTestImageMirrorLocation)
255+
targetMappings := image.GetMappedImages(openshiftDefaults, source)
256+
257+
for from, to := range targetMappings {
258+
if from == to {
259+
continue
260+
}
261+
if covered.Has(to) {
262+
continue
263+
}
264+
covered.Insert(to)
265+
from := sourceMappings[from]
266+
openshiftUpdated[from] = to
267+
}
268+
}
269+
270+
covered := sets.NewString()
271+
var lines []string
272+
for i := range updatedImageSets {
273+
for imageID := range updatedImageSets[i] {
274+
a, b := defaultImageSets[i][imageID], updatedImageSets[i][imageID]
275+
from, to := a.GetE2EImage(), b.GetE2EImage()
276+
if from == to {
277+
continue
278+
}
279+
if covered.Has(from) {
280+
continue
281+
}
282+
covered.Insert(from)
283+
lines = append(lines, fmt.Sprintf("%s %s%s", from, prefix, to))
284+
}
285+
}
286+
287+
for from, to := range openshiftUpdated {
288+
if from == to {
289+
continue
290+
}
291+
if covered.Has(from) {
292+
continue
293+
}
294+
covered.Insert(from)
295+
lines = append(lines, fmt.Sprintf("%s %s%s", from, prefix, to))
296+
}
297+
298+
sort.Strings(lines)
299+
return lines, nil
300+
}
301+
302+
func covertMappedImageToImageConfig(image extension.Image) k8simage.Config {
303+
imageConfig := k8simage.Config{}
304+
imageConfig.SetName(image.Name)
305+
imageConfig.SetVersion(image.Version)
306+
imageConfig.SetRegistry(image.Registry)
307+
308+
return imageConfig
309+
}

0 commit comments

Comments
 (0)