Merge pull request #965 from tonistiigi/bake-context-validation

bake: additional support for named context on remote inputs
pull/969/head
Tõnis Tiigi 3 years ago committed by GitHub
commit 6ac01ec9ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
@ -715,6 +716,21 @@ func updateContext(t *build.Inputs, inp *Input) {
if inp == nil || inp.State == nil { if inp == nil || inp.State == nil {
return return
} }
for k, v := range t.NamedContexts {
if v.Path == "." {
t.NamedContexts[k] = build.NamedContext{Path: inp.URL}
}
if strings.HasPrefix(v.Path, "cwd://") || strings.HasPrefix(v.Path, "target:") || strings.HasPrefix(v.Path, "docker-image:") {
continue
}
if IsRemoteURL(v.Path) {
continue
}
st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/"), llb.WithCustomNamef("set context %s to %s", k, v.Path))
t.NamedContexts[k] = build.NamedContext{State: &st}
}
if t.ContextPath == "." { if t.ContextPath == "." {
t.ContextPath = inp.URL t.ContextPath = inp.URL
return return
@ -729,6 +745,59 @@ func updateContext(t *build.Inputs, inp *Input) {
t.ContextState = &st t.ContextState = &st
} }
// validateContextsEntitlements is a basic check to ensure contexts do not
// escape local directories when loaded from remote sources. This is to be
// replaced with proper entitlements support in the future.
func validateContextsEntitlements(t build.Inputs, inp *Input) error {
if inp == nil || inp.State == nil {
return nil
}
if v, ok := os.LookupEnv("BAKE_ALLOW_REMOTE_FS_ACCESS"); ok {
if vv, _ := strconv.ParseBool(v); vv {
return nil
}
}
if t.ContextState == nil {
if err := checkPath(t.ContextPath); err != nil {
return err
}
}
for _, v := range t.NamedContexts {
if v.State != nil {
continue
}
if err := checkPath(v.Path); err != nil {
return err
}
}
return nil
}
func checkPath(p string) error {
if IsRemoteURL(p) || strings.HasPrefix(p, "target:") || strings.HasPrefix(p, "docker-image:") {
return nil
}
p, err := filepath.EvalSymlinks(p)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
wd, err := os.Getwd()
if err != nil {
return err
}
rel, err := filepath.Rel(wd, p)
if err != nil {
return err
}
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return errors.Errorf("path %s is outside of the working directory, please set BAKE_ALLOW_REMOTE_FS_ACCESS=1", p)
}
return nil
}
func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if v := t.Context; v != nil && *v == "-" { if v := t.Context; v != nil && *v == "-" {
return nil, errors.Errorf("context from stdin not allowed in bake") return nil, errors.Errorf("context from stdin not allowed in bake")
@ -769,7 +838,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
bi := build.Inputs{ bi := build.Inputs{
ContextPath: contextPath, ContextPath: contextPath,
DockerfilePath: dockerfilePath, DockerfilePath: dockerfilePath,
NamedContexts: t.Contexts, NamedContexts: toNamedContexts(t.Contexts),
} }
if t.DockerfileInline != nil { if t.DockerfileInline != nil {
bi.DockerfileInline = *t.DockerfileInline bi.DockerfileInline = *t.DockerfileInline
@ -778,6 +847,15 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if strings.HasPrefix(bi.ContextPath, "cwd://") { if strings.HasPrefix(bi.ContextPath, "cwd://") {
bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://")) bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://"))
} }
for k, v := range bi.NamedContexts {
if strings.HasPrefix(v.Path, "cwd://") {
bi.NamedContexts[k] = build.NamedContext{Path: path.Clean(strings.TrimPrefix(v.Path, "cwd://"))}
}
}
if err := validateContextsEntitlements(bi, inp); err != nil {
return nil, err
}
t.Context = &bi.ContextPath t.Context = &bi.ContextPath
@ -903,3 +981,11 @@ func sliceEqual(s1, s2 []string) bool {
} }
return true return true
} }
func toNamedContexts(m map[string]string) map[string]build.NamedContext {
m2 := make(map[string]build.NamedContext, len(m))
for k, v := range m {
m2[k] = build.NamedContext{Path: v}
}
return m2
}

@ -386,8 +386,8 @@ func TestReadContexts(t *testing.T) {
ctxs := bo["app"].Inputs.NamedContexts ctxs := bo["app"].Inputs.NamedContexts
require.Equal(t, 2, len(ctxs)) require.Equal(t, 2, len(ctxs))
require.Equal(t, "baz", ctxs["foo"]) require.Equal(t, "baz", ctxs["foo"].Path)
require.Equal(t, "def", ctxs["abc"]) require.Equal(t, "def", ctxs["abc"].Path)
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo=bay", "base.contexts.ghi=jkl"}, nil) m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo=bay", "base.contexts.ghi=jkl"}, nil)
require.NoError(t, err) require.NoError(t, err)
@ -402,9 +402,9 @@ func TestReadContexts(t *testing.T) {
ctxs = bo["app"].Inputs.NamedContexts ctxs = bo["app"].Inputs.NamedContexts
require.Equal(t, 3, len(ctxs)) require.Equal(t, 3, len(ctxs))
require.Equal(t, "bay", ctxs["foo"]) require.Equal(t, "bay", ctxs["foo"].Path)
require.Equal(t, "def", ctxs["abc"]) require.Equal(t, "def", ctxs["abc"].Path)
require.Equal(t, "jkl", ctxs["ghi"]) require.Equal(t, "jkl", ctxs["ghi"].Path)
// test resetting base values // test resetting base values
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo="}, nil) m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo="}, nil)
@ -419,7 +419,7 @@ func TestReadContexts(t *testing.T) {
ctxs = bo["app"].Inputs.NamedContexts ctxs = bo["app"].Inputs.NamedContexts
require.Equal(t, 1, len(ctxs)) require.Equal(t, 1, len(ctxs))
require.Equal(t, "def", ctxs["abc"]) require.Equal(t, "def", ctxs["abc"].Path)
} }
func TestReadContextFromTargetUnknown(t *testing.T) { func TestReadContextFromTargetUnknown(t *testing.T) {

@ -84,7 +84,12 @@ type Inputs struct {
InStream io.Reader InStream io.Reader
ContextState *llb.State ContextState *llb.State
DockerfileInline string DockerfileInline string
NamedContexts map[string]string NamedContexts map[string]NamedContext
}
type NamedContext struct {
Path string
State *llb.State
} }
type DriverInfo struct { type DriverInfo struct {
@ -1160,11 +1165,20 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr
for k, v := range inp.NamedContexts { for k, v := range inp.NamedContexts {
target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward"
if urlutil.IsGitURL(v) || urlutil.IsURL(v) || strings.HasPrefix(v, "docker-image://") || strings.HasPrefix(v, "target:") { if v.State != nil {
target.FrontendAttrs["context:"+k] = v target.FrontendAttrs["context:"+k] = "input:" + k
if target.FrontendInputs == nil {
target.FrontendInputs = make(map[string]llb.State)
}
target.FrontendInputs[k] = *v.State
continue
}
if urlutil.IsGitURL(v.Path) || urlutil.IsURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") {
target.FrontendAttrs["context:"+k] = v.Path
continue continue
} }
st, err := os.Stat(v) st, err := os.Stat(v.Path)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get build context %v", k) return nil, errors.Wrapf(err, "failed to get build context %v", k)
} }
@ -1175,7 +1189,7 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr
if k == "context" || k == "dockerfile" { if k == "context" || k == "dockerfile" {
localName = "_" + k // underscore to avoid collisions localName = "_" + k // underscore to avoid collisions
} }
target.LocalDirs[localName] = v target.LocalDirs[localName] = v.Path
target.FrontendAttrs["context:"+k] = "local:" + localName target.FrontendAttrs["context:"+k] = "local:" + localName
} }

@ -480,11 +480,11 @@ func listToMap(values []string, defaultEnv bool) map[string]string {
return result return result
} }
func parseContextNames(values []string) (map[string]string, error) { func parseContextNames(values []string) (map[string]build.NamedContext, error) {
if len(values) == 0 { if len(values) == 0 {
return nil, nil return nil, nil
} }
result := make(map[string]string, len(values)) result := make(map[string]build.NamedContext, len(values))
for _, value := range values { for _, value := range values {
kv := strings.SplitN(value, "=", 2) kv := strings.SplitN(value, "=", 2)
if len(kv) != 2 { if len(kv) != 2 {
@ -495,7 +495,7 @@ func parseContextNames(values []string) (map[string]string, error) {
return nil, errors.Wrapf(err, "invalid context name %s", kv[0]) return nil, errors.Wrapf(err, "invalid context name %s", kv[0])
} }
name := strings.TrimSuffix(reference.FamiliarString(named), ":latest") name := strings.TrimSuffix(reference.FamiliarString(named), ":latest")
result[name] = kv[1] result[name] = build.NamedContext{Path: kv[1]}
} }
return result, nil return result, nil
} }

Loading…
Cancel
Save