Skip to content

Commit

Permalink
Merge pull request #2847 from cpuguy83/fix_dockerimage_context
Browse files Browse the repository at this point in the history
Resolve docker-image named context config AOT
  • Loading branch information
tonistiigi authored May 5, 2022
2 parents e071c35 + 74eec1e commit 4c95183
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 12 deletions.
39 changes: 32 additions & 7 deletions frontend/dockerfile/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,8 +787,8 @@ func warnOpts(sm *llb.SourceMap, r *parser.Range, detail [][]byte, url string) c
return opts
}

func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Context, string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
return func(ctx context.Context, name string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Context, string, string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
return func(ctx context.Context, name, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "invalid context name %s", name)
Expand All @@ -801,19 +801,19 @@ func contextByNameFunc(c client.Client, p *ocispecs.Platform) func(context.Conte
}
if p != nil {
name := name + "::" + platforms.Format(platforms.Normalize(*p))
st, img, bi, err := contextByName(ctx, c, name, p)
st, img, bi, err := contextByName(ctx, c, name, p, resolveMode)
if err != nil {
return nil, nil, nil, err
}
if st != nil {
return st, img, bi, nil
}
}
return contextByName(ctx, c, name, p)
return contextByName(ctx, c, name, p, resolveMode)
}
}

func contextByName(ctx context.Context, c client.Client, name string, platform *ocispecs.Platform) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
func contextByName(ctx context.Context, c client.Client, name string, platform *ocispecs.Platform, resolveMode string) (*llb.State, *dockerfile2llb.Image, *binfotypes.BuildInfo, error) {
opts := c.BuildOpts().Opts
v, ok := opts["context:"+name]
if !ok {
Expand All @@ -829,13 +829,38 @@ func contextByName(ctx context.Context, c client.Client, name string, platform *
ref := strings.TrimPrefix(vv[1], "//")
imgOpt := []llb.ImageOption{
llb.WithCustomName("[context " + name + "] " + ref),
llb.WithMetaResolver(c),
}
if platform != nil {
imgOpt = append(imgOpt, llb.Platform(*platform))
}

named, err := reference.ParseNormalizedNamed(ref)
if err != nil {
return nil, nil, nil, err
}

named = reference.TagNameOnly(named)

_, data, err := c.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{
Platform: platform,
ResolveMode: resolveMode,
LogName: fmt.Sprintf("[context %s] load metadata for %s", name, ref),
})
if err != nil {
return nil, nil, nil, err
}

var img dockerfile2llb.Image
if err := json.Unmarshal(data, &img); err != nil {
return nil, nil, nil, err
}

st := llb.Image(ref, imgOpt...)
return &st, nil, nil, nil
st, err = st.WithImageConfig(data)
if err != nil {
return nil, nil, nil, err
}
return &st, &img, nil, nil
case "git":
st, ok := detectGitContext(v, "1")
if !ok {
Expand Down
10 changes: 5 additions & 5 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ type ConvertOpt struct {
SourceMap *llb.SourceMap
Hostname string
Warn func(short, url string, detail [][]byte, location *parser.Range)
ContextByName func(context.Context, string) (*llb.State, *Image, *binfotypes.BuildInfo, error)
ContextByName func(ctx context.Context, name, resolveMode string) (*llb.State, *Image, *binfotypes.BuildInfo, error)
}

func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, *binfotypes.BuildInfo, error) {
buildInfo := &binfotypes.BuildInfo{}
contextByName := opt.ContextByName
opt.ContextByName = func(ctx context.Context, name string) (*llb.State, *Image, *binfotypes.BuildInfo, error) {
opt.ContextByName = func(ctx context.Context, name, resolveMode string) (*llb.State, *Image, *binfotypes.BuildInfo, error) {
if !strings.EqualFold(name, "scratch") && !strings.EqualFold(name, "context") {
if contextByName != nil {
st, img, bi, err := contextByName(ctx, name)
st, img, bi, err := contextByName(ctx, name, resolveMode)
if err != nil {
return nil, nil, nil, err
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
}

if st.Name != "" {
s, img, bi, err := opt.ContextByName(ctx, st.Name)
s, img, bi, err := opt.ContextByName(ctx, st.Name, opt.ImageResolveMode.String())
if err != nil {
return nil, nil, nil, err
}
Expand Down Expand Up @@ -308,7 +308,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
d.stage.BaseName = reference.TagNameOnly(ref).String()

var isScratch bool
st, img, bi, err := opt.ContextByName(ctx, d.stage.BaseName)
st, img, bi, err := opt.ContextByName(ctx, d.stage.BaseName, opt.ImageResolveMode.String())
if err != nil {
return err
}
Expand Down
90 changes: 90 additions & 0 deletions frontend/dockerfile/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5223,6 +5223,7 @@ COPY --from=base /out /

_, err = f.Solve(sb.Context(), c, client.SolveOpt{
FrontendAttrs: map[string]string{
// Make sure image resolution works as expected, do not add a tag or locator.
"context:busybox": "docker-image://alpine",
},
LocalDirs: map[string]string{
Expand All @@ -5241,6 +5242,95 @@ COPY --from=base /out /
dt, err := os.ReadFile(filepath.Join(destDir, "out"))
require.NoError(t, err)
require.True(t, len(dt) > 0)

// Now test with an image with custom envs
dockerfile = []byte(`
FROM alpine:latest
ENV PATH=/foobar:$PATH
ENV FOOBAR=foobar
`)

dir, err = tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)

registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
target := registry + "/buildkit/testnamedimagecontext:latest"

_, err = f.Solve(sb.Context(), c, client.SolveOpt{
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
Exports: []client.ExportEntry{
{
Type: client.ExporterImage,
Attrs: map[string]string{
"name": target,
"push": "true",
},
},
},
}, nil)
require.NoError(t, err)

dockerfile = []byte(`
FROM busybox AS base
RUN cat /etc/alpine-release > /out
RUN env | grep PATH > /env_path
RUN env | grep FOOBAR > /env_foobar
FROM scratch
COPY --from=base /out /
COPY --from=base /env_path /
COPY --from=base /env_foobar /
`)

dir, err = tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)

f = getFrontend(t, sb)

destDir, err = os.MkdirTemp("", "buildkit")
require.NoError(t, err)
defer os.RemoveAll(destDir)

_, err = f.Solve(sb.Context(), c, client.SolveOpt{
FrontendAttrs: map[string]string{
"context:busybox": "docker-image://" + target,
},
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
Exports: []client.ExportEntry{
{
Type: client.ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)

dt, err = os.ReadFile(filepath.Join(destDir, "out"))
require.NoError(t, err)
require.True(t, len(dt) > 0)

dt, err = os.ReadFile(filepath.Join(destDir, "env_foobar"))
require.NoError(t, err)
require.Equal(t, "FOOBAR=foobar", strings.TrimSpace(string(dt)))

dt, err = os.ReadFile(filepath.Join(destDir, "env_path"))
require.NoError(t, err)
require.Contains(t, string(dt), "/foobar:")
}

func testNamedLocalContext(t *testing.T, sb integration.Sandbox) {
Expand Down

0 comments on commit 4c95183

Please sign in to comment.