Skip to content

Commit 1a43db6

Browse files
authored
Merge pull request moby#4924 from tonistiigi/baseimage-platform
dockerfile: detect base image with wrong platform being used
2 parents 5893a5b + 88b2f70 commit 1a43db6

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
542542
llb.WithCustomName(prefixCommand(d, "FROM "+d.stage.BaseName, opt.MultiPlatformRequested, platform, nil)),
543543
location(opt.SourceMap, d.stage.Location),
544544
)
545+
validateBaseImagePlatform(origName, *platform, d.image.Platform, d.stage.Location, opt.Warn)
545546
}
546547
d.platform = platform
547548
return nil
@@ -2227,3 +2228,12 @@ func wrapSuggestAny(err error, keys map[string]struct{}, options []string) error
22272228
}
22282229
return err
22292230
}
2231+
2232+
func validateBaseImagePlatform(name string, expected, actual ocispecs.Platform, location []parser.Range, warn linter.LintWarnFunc) {
2233+
if expected.OS != actual.OS || expected.Architecture != actual.Architecture {
2234+
expectedStr := platforms.Format(platforms.Normalize(expected))
2235+
actualStr := platforms.Format(platforms.Normalize(actual))
2236+
msg := linter.RuleInvalidBaseImagePlatform.Format(name, expectedStr, actualStr)
2237+
linter.RuleInvalidBaseImagePlatform.Run(warn, location, msg)
2238+
}
2239+
}

frontend/dockerfile/dockerfile_lint_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var lintTests = integration.TestFuncs(
3737
testUnmatchedVars,
3838
testMultipleInstructionsDisallowed,
3939
testLegacyKeyValueFormat,
40+
testBaseImagePlatformMismatch,
4041
)
4142

4243
func testStageName(t *testing.T, sb integration.Sandbox) {
@@ -800,10 +801,15 @@ func checkProgressStream(t *testing.T, sb integration.Sandbox, lintTest *lintTes
800801

801802
f := getFrontend(t, sb)
802803

803-
_, err := f.Solve(sb.Context(), lintTest.Client, client.SolveOpt{
804-
FrontendAttrs: map[string]string{
804+
attrs := lintTest.FrontendAttrs
805+
if attrs == nil {
806+
attrs = map[string]string{
805807
"platform": "linux/amd64,linux/arm64",
806-
},
808+
}
809+
}
810+
811+
_, err := f.Solve(sb.Context(), lintTest.Client, client.SolveOpt{
812+
FrontendAttrs: attrs,
807813
LocalMounts: map[string]fsutil.FS{
808814
dockerui.DefaultLocalNameDockerfile: lintTest.TmpDir,
809815
dockerui.DefaultLocalNameContext: lintTest.TmpDir,
@@ -915,4 +921,5 @@ type lintTestParams struct {
915921
StreamBuildErr string
916922
UnmarshalBuildErr string
917923
BuildErrLocation int32
924+
FrontendAttrs map[string]string
918925
}

frontend/dockerfile/dockerfile_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7167,6 +7167,80 @@ func testSourcePolicyWithNamedContext(t *testing.T, sb integration.Sandbox) {
71677167
require.Equal(t, "foo", string(dt))
71687168
}
71697169

7170+
func testBaseImagePlatformMismatch(t *testing.T, sb integration.Sandbox) {
7171+
integration.SkipOnPlatform(t, "windows")
7172+
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush)
7173+
ctx := sb.Context()
7174+
7175+
c, err := client.New(ctx, sb.Address())
7176+
require.NoError(t, err)
7177+
defer c.Close()
7178+
7179+
registry, err := sb.NewRegistry()
7180+
if errors.Is(err, integration.ErrRequirements) {
7181+
t.Skip(err.Error())
7182+
}
7183+
require.NoError(t, err)
7184+
7185+
f := getFrontend(t, sb)
7186+
7187+
dockerfile := []byte(`
7188+
FROM scratch
7189+
COPY foo /foo
7190+
`)
7191+
dir := integration.Tmpdir(
7192+
t,
7193+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
7194+
fstest.CreateFile("foo", []byte("test"), 0644),
7195+
)
7196+
7197+
// choose target platform that is different from the current platform
7198+
targetPlatform := runtime.GOOS + "/arm64"
7199+
if runtime.GOARCH == "arm64" {
7200+
targetPlatform = runtime.GOOS + "/amd64"
7201+
}
7202+
7203+
target := registry + "/buildkit/testbaseimageplatform:latest"
7204+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
7205+
LocalMounts: map[string]fsutil.FS{
7206+
dockerui.DefaultLocalNameDockerfile: dir,
7207+
dockerui.DefaultLocalNameContext: dir,
7208+
},
7209+
FrontendAttrs: map[string]string{
7210+
"platform": targetPlatform,
7211+
},
7212+
Exports: []client.ExportEntry{
7213+
{
7214+
Type: client.ExporterImage,
7215+
Attrs: map[string]string{
7216+
"name": target,
7217+
"push": "true",
7218+
},
7219+
},
7220+
},
7221+
}, nil)
7222+
require.NoError(t, err)
7223+
7224+
dockerfile = []byte(fmt.Sprintf(`
7225+
FROM %s
7226+
ENV foo=bar
7227+
`, target))
7228+
7229+
checkLinterWarnings(t, sb, &lintTestParams{
7230+
Dockerfile: dockerfile,
7231+
Warnings: []expectedLintWarning{
7232+
{
7233+
RuleName: "InvalidBaseImagePlatform",
7234+
Description: "Base image platform does not match expected target platform",
7235+
Detail: fmt.Sprintf("Base image %s was pulled with platform %q, expected %q for current build", target, targetPlatform, runtime.GOOS+"/"+runtime.GOARCH),
7236+
Level: 1,
7237+
Line: 2,
7238+
},
7239+
},
7240+
FrontendAttrs: map[string]string{},
7241+
})
7242+
}
7243+
71707244
func runShell(dir string, cmds ...string) error {
71717245
for _, args := range cmds {
71727246
var cmd *exec.Cmd

frontend/dockerfile/linter/ruleset.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,11 @@ var (
120120
return fmt.Sprintf("\"%s key=value\" should be used instead of legacy \"%s key value\" format", cmdName, cmdName)
121121
},
122122
}
123+
RuleInvalidBaseImagePlatform = LinterRule[func(string, string, string) string]{
124+
Name: "InvalidBaseImagePlatform",
125+
Description: "Base image platform does not match expected target platform",
126+
Format: func(image, expected, actual string) string {
127+
return fmt.Sprintf("Base image %s was pulled with platform %q, expected %q for current build", image, actual, expected)
128+
},
129+
}
123130
)

0 commit comments

Comments
 (0)