mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 16:13:45 +08:00 
			
		
		
		
	Merge pull request #965 from tonistiigi/bake-context-validation
bake: additional support for named context on remote inputs
This commit is contained in:
		
							
								
								
									
										88
									
								
								bake/bake.go
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								bake/bake.go
									
									
									
									
									
								
							| @@ -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 | 			continue | ||||||
| 		} | 		} | ||||||
| 		st, err := os.Stat(v) |  | ||||||
|  | 		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 | ||||||
|  | 		} | ||||||
|  | 		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 | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tõnis Tiigi
					Tõnis Tiigi