Skip to content

Commit 190ad0e

Browse files
authored
Validate index architectures match children (#1776)
Now crane validate will validate the entire remote index, including that there are not any mismatched architectures. If you pass a --platform flag, it will behave how it previously behaved.
1 parent a54d642 commit 190ad0e

File tree

2 files changed

+94
-17
lines changed

2 files changed

+94
-17
lines changed

cmd/crane/cmd/validate.go

+40-17
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"fmt"
1919

2020
"github.com/google/go-containerregistry/pkg/crane"
21-
v1 "github.com/google/go-containerregistry/pkg/v1"
2221
"github.com/google/go-containerregistry/pkg/v1/tarball"
2322
"github.com/google/go-containerregistry/pkg/v1/validate"
2423
"github.com/spf13/cobra"
@@ -36,28 +35,56 @@ func NewCmdValidate(options *[]crane.Option) *cobra.Command {
3635
Short: "Validate that an image is well-formed",
3736
Args: cobra.ExactArgs(0),
3837
RunE: func(cmd *cobra.Command, args []string) error {
39-
for flag, maker := range map[string]func(string, ...crane.Option) (v1.Image, error){
40-
tarballPath: makeTarball,
41-
remoteRef: crane.Pull,
42-
} {
43-
if flag == "" {
44-
continue
45-
}
46-
img, err := maker(flag, *options...)
38+
if tarballPath != "" {
39+
img, err := tarball.ImageFromPath(tarballPath, nil)
4740
if err != nil {
48-
return fmt.Errorf("failed to read image %s: %w", flag, err)
41+
return fmt.Errorf("failed to read image %s: %w", tarballPath, err)
4942
}
50-
5143
opt := []validate.Option{}
5244
if fast {
5345
opt = append(opt, validate.Fast)
5446
}
5547
if err := validate.Image(img, opt...); err != nil {
56-
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", flag, err)
48+
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", tarballPath, err)
5749
return err
5850
}
59-
fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", flag)
51+
fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", tarballPath)
52+
}
53+
54+
if remoteRef != "" {
55+
rmt, err := crane.Get(remoteRef, *options...)
56+
if err != nil {
57+
return fmt.Errorf("failed to read image %s: %w", remoteRef, err)
58+
}
59+
60+
o := crane.GetOptions(*options...)
61+
62+
opt := []validate.Option{}
63+
if fast {
64+
opt = append(opt, validate.Fast)
65+
}
66+
if rmt.MediaType.IsIndex() && o.Platform == nil {
67+
idx, err := rmt.ImageIndex()
68+
if err != nil {
69+
return fmt.Errorf("reading index: %w", err)
70+
}
71+
if err := validate.Index(idx, opt...); err != nil {
72+
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err)
73+
return err
74+
}
75+
} else {
76+
img, err := rmt.Image()
77+
if err != nil {
78+
return fmt.Errorf("reading image: %w", err)
79+
}
80+
if err := validate.Image(img, opt...); err != nil {
81+
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err)
82+
return err
83+
}
84+
}
85+
fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", remoteRef)
6086
}
87+
6188
return nil
6289
},
6390
}
@@ -67,7 +94,3 @@ func NewCmdValidate(options *[]crane.Option) *cobra.Command {
6794

6895
return validateCmd
6996
}
70-
71-
func makeTarball(path string, _ ...crane.Option) (v1.Image, error) {
72-
return tarball.ImageFromPath(path, nil)
73-
}

pkg/v1/validate/index.go

+54
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ func validateChildren(idx v1.ImageIndex, opt ...Option) error {
7979
if err := validateMediaType(img, desc.MediaType); err != nil {
8080
errs = append(errs, fmt.Sprintf("failed to validate image MediaType[%d](%s): %v", i, desc.Digest, err))
8181
}
82+
if err := validatePlatform(img, desc.Platform); err != nil {
83+
errs = append(errs, fmt.Sprintf("failed to validate image platform[%d](%s): %v", i, desc.Digest, err))
84+
}
8285
default:
8386
// Workaround for #819.
8487
if wl, ok := idx.(withLayer); ok {
@@ -173,3 +176,54 @@ func validateIndexManifest(idx v1.ImageIndex) error {
173176

174177
return nil
175178
}
179+
180+
func validatePlatform(img v1.Image, want *v1.Platform) error {
181+
if want == nil {
182+
return nil
183+
}
184+
185+
cf, err := img.ConfigFile()
186+
if err != nil {
187+
return err
188+
}
189+
190+
got := cf.Platform()
191+
192+
if got == nil {
193+
return fmt.Errorf("config file missing platform fields")
194+
}
195+
196+
if got.Equals(*want) {
197+
return nil
198+
}
199+
200+
errs := []string{}
201+
202+
if got.OS != want.OS {
203+
errs = append(errs, fmt.Sprintf("mismatched OS: %s != %s", got.OS, want.OS))
204+
}
205+
206+
if got.Architecture != want.Architecture {
207+
errs = append(errs, fmt.Sprintf("mismatched Architecture: %s != %s", got.Architecture, want.Architecture))
208+
}
209+
210+
if got.OSVersion != want.OSVersion {
211+
errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion))
212+
}
213+
214+
if got.OSVersion != want.OSVersion {
215+
errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion))
216+
}
217+
218+
if len(errs) == 0 {
219+
// If we got here, some features might be mismatched. Just add those...
220+
if len(got.Features) != 0 || len(want.Features) != 0 {
221+
errs = append(errs, fmt.Sprintf("mismatched Features: %v, %v", got.Features, want.Features))
222+
}
223+
if len(got.OSFeatures) != 0 || len(want.OSFeatures) != 0 {
224+
errs = append(errs, fmt.Sprintf("mismatched OSFeatures: %v, %v", got.OSFeatures, want.OSFeatures))
225+
}
226+
}
227+
228+
return errors.New(strings.Join(errs, "\n"))
229+
}

0 commit comments

Comments
 (0)