Compare commits

..

11 Commits

Author SHA1 Message Date
CrazyMax
48d6a3927a Merge pull request #2867 from crazy-max/v0.19_backport_entitlement-path-fix
[v0.19 backport] bake: change evaluation of entitlement paths
2024-12-17 12:25:19 +01:00
Tonis Tiigi
2bd4aefb9b add additional test coverage for FS entitlement paths
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-12-17 10:16:56 +01:00
Tonis Tiigi
f1b3cf74eb bake: change evaluation of entitlement paths
Currently, to compare the local path used by bake against the paths allowed
by entitlements, symlinks were evaluated for path normalization so that the
local path used by build was allowed to not exist while the path allowed by
entitlement needed to exist. If the path used by the build did not exist,
then the deepest existing parent path was used instead. This was concistent
with entitlement rules as that parent path would be the actual path access
is needed.

This raised an issue with `--set` if one provides a non-existing path as
an argument, as these paths are supposed to be allowed automatically. With
the above restrictions set to allowed paths, this meant the build would fail
as it can't grant entitlement to the non-existing paths.

This changes the evaluation logic for allowing paths so that they do not
need to exist. If such a case appears, then the path is evaluated to the
last component that exists, and then the rest of the path is appended as is.

This means that for example, if `output = /tmp/out/foo/` is set in HCL
and `/tmp` is the last component that exists then invoking build with
`--allow fs.write=/tmp/out/foo` will not fail with stat error anymore
but will fail in entitlements validation as build would also need to
write `/tmp/out` that is not inside the allowed `/tmp/out/foo` path. The
same would apply to `--set` as well so that if it points to
a non-existing path, then an additional `--allow` rule is needed
providing access to writing to the last existing component of that path.
This may or may not be unexpected.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-12-17 10:15:21 +01:00
Tõnis Tiigi
62d486d5a5 Merge pull request #2861 from tonistiigi/v0.19-set-empty-eof
[v0.19] bake: remove empty values set by --set
2024-12-16 10:16:22 -08:00
CrazyMax
afc9cebb48 bake: test empty override
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2024-12-16 18:35:14 +01:00
Tonis Tiigi
9a9dd4e87e [v0.19] bake: remove empty values set by --set
These parser functions are called for `--set` to
resolve entitlement paths that would be automatically added
and will fail for empty value.

The empty values would already be ignored but in v0.19 is
done after merging the `--set` values and then calling
parse again.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-12-16 09:12:36 -08:00
CrazyMax
85acd30ed7 Merge pull request #2862 from crazy-max/v0.19_pick-xx-update
[v0.19 backport] update xx to v1.6.1
2024-12-16 13:20:09 +01:00
Tonis Tiigi
90393e68a4 update xx to v1.6.1
Fixes compatibility issues with Alpine 3.21

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-12-16 10:39:49 +01:00
Tõnis Tiigi
1fc5647dc2 Merge pull request #2846 from tonistiigi/v0.19.2-picks
[v0.19] v0.19.2 cherry-picks
2024-12-06 14:45:47 -08:00
Tonis Tiigi
b2c0c26c26 bake: allow entitlements from overrides automatically
If override specifies a path, mark it automatically allowed
so there is no need to use duplicate flags for defining the
same feature.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit dd596d6542)
2024-12-06 14:15:46 -08:00
Tonis Tiigi
c30db6a955 bake: fix entitlements path checks for local outputs
Previous check based on dest attributes was not correct
as the attributes already get converted before validation happens.

Because the local path is not preserved for single-file
outputs and gets replaced by io.Writer, a temporary array variable
was needed. This value should instead be added to ExportEntry
struct in BuildKit in future revision.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit c6e403ad7f)
2024-12-06 14:02:04 -08:00
14 changed files with 557 additions and 179 deletions

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
ARG GO_VERSION=1.23 ARG GO_VERSION=1.23
ARG XX_VERSION=1.5.0 ARG XX_VERSION=1.6.1
# for testing # for testing
ARG DOCKER_VERSION=27.4.0-rc.2 ARG DOCKER_VERSION=27.4.0-rc.2

View File

@@ -193,7 +193,7 @@ func ListTargets(files []File) ([]string, error) {
return dedupSlice(targets), nil return dedupSlice(targets), nil
} }
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, map[string]*Group, error) { func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string, ent *EntitlementConf) (map[string]*Target, map[string]*Group, error) {
c, _, err := ParseFiles(files, defaults) c, _, err := ParseFiles(files, defaults)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -212,7 +212,7 @@ func ReadTargets(ctx context.Context, files []File, targets, overrides []string,
for _, target := range targets { for _, target := range targets {
ts, gs := c.ResolveGroup(target) ts, gs := c.ResolveGroup(target)
for _, tname := range ts { for _, tname := range ts {
t, err := c.ResolveTarget(tname, o) t, err := c.ResolveTarget(tname, o, ent)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -244,7 +244,7 @@ func ReadTargets(ctx context.Context, files []File, targets, overrides []string,
} }
for name, t := range m { for name, t := range m {
if err := c.loadLinks(name, t, m, o, nil); err != nil { if err := c.loadLinks(name, t, m, o, nil, ent); err != nil {
return nil, nil, err return nil, nil, err
} }
} }
@@ -476,7 +476,7 @@ func (c Config) expandTargets(pattern string) ([]string, error) {
return names, nil return names, nil
} }
func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[string]map[string]Override, visited []string) error { func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[string]map[string]Override, visited []string, ent *EntitlementConf) error {
visited = append(visited, name) visited = append(visited, name)
for _, v := range t.Contexts { for _, v := range t.Contexts {
if strings.HasPrefix(v, "target:") { if strings.HasPrefix(v, "target:") {
@@ -492,7 +492,7 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
t2, ok := m[target] t2, ok := m[target]
if !ok { if !ok {
var err error var err error
t2, err = c.ResolveTarget(target, o) t2, err = c.ResolveTarget(target, o, ent)
if err != nil { if err != nil {
return err return err
} }
@@ -500,7 +500,7 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
t2.linked = true t2.linked = true
m[target] = t2 m[target] = t2
} }
if err := c.loadLinks(target, t2, m, o, visited); err != nil { if err := c.loadLinks(target, t2, m, o, visited, ent); err != nil {
return err return err
} }
@@ -627,8 +627,8 @@ func (c Config) group(name string, visited map[string]visit) ([]string, []string
return targets, groups return targets, groups
} }
func (c Config) ResolveTarget(name string, overrides map[string]map[string]Override) (*Target, error) { func (c Config) ResolveTarget(name string, overrides map[string]map[string]Override, ent *EntitlementConf) (*Target, error) {
t, err := c.target(name, map[string]*Target{}, overrides) t, err := c.target(name, map[string]*Target{}, overrides, ent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -644,7 +644,7 @@ func (c Config) ResolveTarget(name string, overrides map[string]map[string]Overr
return t, nil return t, nil
} }
func (c Config) target(name string, visited map[string]*Target, overrides map[string]map[string]Override) (*Target, error) { func (c Config) target(name string, visited map[string]*Target, overrides map[string]map[string]Override, ent *EntitlementConf) (*Target, error) {
if t, ok := visited[name]; ok { if t, ok := visited[name]; ok {
return t, nil return t, nil
} }
@@ -661,7 +661,7 @@ func (c Config) target(name string, visited map[string]*Target, overrides map[st
} }
tt := &Target{} tt := &Target{}
for _, name := range t.Inherits { for _, name := range t.Inherits {
t, err := c.target(name, visited, overrides) t, err := c.target(name, visited, overrides, ent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -673,7 +673,7 @@ func (c Config) target(name string, visited map[string]*Target, overrides map[st
m.Merge(tt) m.Merge(tt)
m.Merge(t) m.Merge(t)
tt = m tt = m
if err := tt.AddOverrides(overrides[name]); err != nil { if err := tt.AddOverrides(overrides[name], ent); err != nil {
return nil, err return nil, err
} }
tt.normalize() tt.normalize()
@@ -856,7 +856,7 @@ func (t *Target) Merge(t2 *Target) {
t.Inherits = append(t.Inherits, t2.Inherits...) t.Inherits = append(t.Inherits, t2.Inherits...)
} }
func (t *Target) AddOverrides(overrides map[string]Override) error { func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementConf) error {
for key, o := range overrides { for key, o := range overrides {
value := o.Value value := o.Value
keys := strings.SplitN(key, ".", 2) keys := strings.SplitN(key, ".", 2)
@@ -893,22 +893,76 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
t.Tags = o.ArrValue t.Tags = o.ArrValue
case "cache-from": case "cache-from":
t.CacheFrom = o.ArrValue t.CacheFrom = o.ArrValue
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil {
return err
}
for _, c := range cacheFrom {
if c.Type == "local" {
if v, ok := c.Attrs["src"]; ok {
ent.FSRead = append(ent.FSRead, v)
}
}
}
case "cache-to": case "cache-to":
t.CacheTo = o.ArrValue t.CacheTo = o.ArrValue
cacheTo, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil {
return err
}
for _, c := range cacheTo {
if c.Type == "local" {
if v, ok := c.Attrs["dest"]; ok {
ent.FSWrite = append(ent.FSWrite, v)
}
}
}
case "target": case "target":
t.Target = &value t.Target = &value
case "call": case "call":
t.Call = &value t.Call = &value
case "secrets": case "secrets":
t.Secrets = o.ArrValue t.Secrets = o.ArrValue
secrets, err := buildflags.ParseSecretSpecs(o.ArrValue)
if err != nil {
return errors.Wrap(err, "invalid value for outputs")
}
for _, s := range secrets {
if s.FilePath != "" {
ent.FSRead = append(ent.FSRead, s.FilePath)
}
}
case "ssh": case "ssh":
t.SSH = o.ArrValue t.SSH = o.ArrValue
ssh, err := buildflags.ParseSSHSpecs(o.ArrValue)
if err != nil {
return errors.Wrap(err, "invalid value for outputs")
}
for _, s := range ssh {
ent.FSRead = append(ent.FSRead, s.Paths...)
}
case "platform": case "platform":
t.Platforms = o.ArrValue t.Platforms = o.ArrValue
case "output": case "output":
t.Outputs = o.ArrValue t.Outputs = o.ArrValue
outputs, err := buildflags.ParseExports(o.ArrValue)
if err != nil {
return errors.Wrap(err, "invalid value for outputs")
}
for _, o := range outputs {
if o.Destination != "" {
ent.FSWrite = append(ent.FSWrite, o.Destination)
}
}
case "entitlements": case "entitlements":
t.Entitlements = append(t.Entitlements, o.ArrValue...) t.Entitlements = append(t.Entitlements, o.ArrValue...)
for _, v := range o.ArrValue {
if v == string(EntitlementKeyNetworkHost) {
ent.NetworkHost = true
} else if v == string(EntitlementKeySecurityInsecure) {
ent.SecurityInsecure = true
}
}
case "annotations": case "annotations":
t.Annotations = append(t.Annotations, o.ArrValue...) t.Annotations = append(t.Annotations, o.ArrValue...)
case "attest": case "attest":
@@ -1329,7 +1383,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
bo.Exports, err = controllerapi.CreateExports(outputs)
bo.Exports, bo.ExportsLocalPathsTemporary, err = controllerapi.CreateExports(outputs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -40,7 +40,7 @@ target "webapp" {
t.Run("NoOverrides", func(t *testing.T) { t.Run("NoOverrides", func(t *testing.T) {
t.Parallel() t.Parallel()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -58,7 +58,7 @@ target "webapp" {
t.Run("InvalidTargetOverrides", func(t *testing.T) { t.Run("InvalidTargetOverrides", func(t *testing.T) {
t.Parallel() t.Parallel()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Equal(t, "could not find any target matching 'nosuchtarget'", err.Error()) require.Equal(t, "could not find any target matching 'nosuchtarget'", err.Error())
}) })
@@ -74,7 +74,7 @@ target "webapp" {
"webapp.args.VAR_FROMENV" + t.Name(), "webapp.args.VAR_FROMENV" + t.Name(),
"webapp.args.VAR_INHERITED=override", "webapp.args.VAR_INHERITED=override",
// not overriding VAR_BOTH on purpose // not overriding VAR_BOTH on purpose
}, nil) }, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile) require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
@@ -103,7 +103,7 @@ target "webapp" {
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{
"webDEP.args.VAR_INHERITED=override", "webDEP.args.VAR_INHERITED=override",
"webDEP.args.VAR_BOTH=override", "webDEP.args.VAR_BOTH=override",
}, nil) }, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ptrstr("override"), m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, ptrstr("override"), m["webapp"].Args["VAR_INHERITED"])
@@ -115,10 +115,10 @@ target "webapp" {
t.Run("ContextOverride", func(t *testing.T) { t.Run("ContextOverride", func(t *testing.T) {
t.Parallel() t.Parallel()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "foo", *m["webapp"].Context) require.Equal(t, "foo", *m["webapp"].Context)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
@@ -127,7 +127,7 @@ target "webapp" {
t.Run("NoCacheOverride", func(t *testing.T) { t.Run("NoCacheOverride", func(t *testing.T) {
t.Parallel() t.Parallel()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, false, *m["webapp"].NoCache) require.Equal(t, false, *m["webapp"].NoCache)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
@@ -135,14 +135,14 @@ target "webapp" {
}) })
t.Run("ShmSizeOverride", func(t *testing.T) { t.Run("ShmSizeOverride", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "256m", *m["webapp"].ShmSize) require.Equal(t, "256m", *m["webapp"].ShmSize)
}) })
t.Run("PullOverride", func(t *testing.T) { t.Run("PullOverride", func(t *testing.T) {
t.Parallel() t.Parallel()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, false, *m["webapp"].Pull) require.Equal(t, false, *m["webapp"].Pull)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
@@ -210,7 +210,7 @@ target "webapp" {
} }
for _, test := range cases { for _, test := range cases {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil) m, g, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil, &EntitlementConf{})
test.check(t, m, g, err) test.check(t, m, g, err)
}) })
} }
@@ -225,7 +225,7 @@ func TestPushOverride(t *testing.T) {
`target "app" { `target "app" {
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,push=true", m["app"].Outputs[0]) require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
@@ -239,7 +239,7 @@ func TestPushOverride(t *testing.T) {
output = ["type=image,compression=zstd"] output = ["type=image,compression=zstd"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0]) require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
@@ -253,7 +253,7 @@ func TestPushOverride(t *testing.T) {
output = ["type=image,compression=zstd"] output = ["type=image,compression=zstd"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0]) require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
@@ -267,7 +267,7 @@ func TestPushOverride(t *testing.T) {
output = ["type=registry"] output = ["type=registry"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=registry", m["app"].Outputs[0]) require.Equal(t, "type=registry", m["app"].Outputs[0])
@@ -281,7 +281,7 @@ func TestPushOverride(t *testing.T) {
output = ["type=registry"] output = ["type=registry"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, len(m["app"].Outputs)) require.Equal(t, 0, len(m["app"].Outputs))
}) })
@@ -296,7 +296,7 @@ func TestPushOverride(t *testing.T) {
target "bar" { target "bar" {
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
require.Equal(t, 1, len(m["foo"].Outputs)) require.Equal(t, 1, len(m["foo"].Outputs))
@@ -314,7 +314,7 @@ func TestLoadOverride(t *testing.T) {
`target "app" { `target "app" {
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=docker", m["app"].Outputs[0]) require.Equal(t, "type=docker", m["app"].Outputs[0])
@@ -328,7 +328,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=docker"] output = ["type=docker"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, []string{"type=docker"}, m["app"].Outputs) require.Equal(t, []string{"type=docker"}, m["app"].Outputs)
@@ -342,7 +342,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=image"] output = ["type=image"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m["app"].Outputs)) require.Equal(t, 2, len(m["app"].Outputs))
require.Equal(t, []string{"type=image", "type=docker"}, m["app"].Outputs) require.Equal(t, []string{"type=image", "type=docker"}, m["app"].Outputs)
@@ -356,7 +356,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=image"] output = ["type=image"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=false"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=false"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, []string{"type=image"}, m["app"].Outputs) require.Equal(t, []string{"type=image"}, m["app"].Outputs)
@@ -370,7 +370,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=registry"] output = ["type=registry"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m["app"].Outputs)) require.Equal(t, 2, len(m["app"].Outputs))
require.Equal(t, []string{"type=registry", "type=docker"}, m["app"].Outputs) require.Equal(t, []string{"type=registry", "type=docker"}, m["app"].Outputs)
@@ -384,7 +384,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=oci,dest=out"] output = ["type=oci,dest=out"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m["app"].Outputs)) require.Equal(t, 2, len(m["app"].Outputs))
require.Equal(t, []string{"type=oci,dest=out", "type=docker"}, m["app"].Outputs) require.Equal(t, []string{"type=oci,dest=out", "type=docker"}, m["app"].Outputs)
@@ -398,7 +398,7 @@ func TestLoadOverride(t *testing.T) {
output = ["type=docker,dest=out"] output = ["type=docker,dest=out"]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m["app"].Outputs)) require.Equal(t, 2, len(m["app"].Outputs))
require.Equal(t, []string{"type=docker,dest=out", "type=docker"}, m["app"].Outputs) require.Equal(t, []string{"type=docker,dest=out", "type=docker"}, m["app"].Outputs)
@@ -414,7 +414,7 @@ func TestLoadOverride(t *testing.T) {
target "bar" { target "bar" {
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
require.Equal(t, 1, len(m["foo"].Outputs)) require.Equal(t, 1, len(m["foo"].Outputs))
@@ -435,7 +435,7 @@ func TestLoadAndPushOverride(t *testing.T) {
target "bar" { target "bar" {
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true", "*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true", "*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
@@ -456,7 +456,7 @@ func TestLoadAndPushOverride(t *testing.T) {
output = [ "type=registry" ] output = [ "type=registry" ]
}`), }`),
} }
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo"}, []string{"*.load=true", "*.push=true"}, nil) m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo"}, []string{"*.load=true", "*.push=true"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -512,7 +512,7 @@ services:
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(m)) require.Equal(t, 3, len(m))
@@ -559,7 +559,7 @@ services:
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"web.app"}, nil, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"web.app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
_, ok := m["web_app"] _, ok := m["web_app"]
@@ -567,7 +567,7 @@ services:
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile) require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"]) require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
m, _, err = ReadTargets(ctx, []File{fp2}, []string{"web_app"}, nil, nil) m, _, err = ReadTargets(ctx, []File{fp2}, []string{"web_app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
_, ok = m["web_app"] _, ok = m["web_app"]
@@ -575,7 +575,7 @@ services:
require.Equal(t, "Dockerfile", *m["web_app"].Dockerfile) require.Equal(t, "Dockerfile", *m["web_app"].Dockerfile)
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"]) require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
_, ok = m["web_app"] _, ok = m["web_app"]
@@ -600,7 +600,7 @@ func TestHCLContextCwdPrefix(t *testing.T) {
}`), }`),
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -631,7 +631,7 @@ func TestHCLDockerfileCwdPrefix(t *testing.T) {
cwd, err := os.Getwd() cwd, err := os.Getwd()
require.NoError(t, err) require.NoError(t, err)
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -662,7 +662,7 @@ func TestOverrideMerge(t *testing.T) {
"app.platform=linux/arm", "app.platform=linux/arm",
"app.platform=linux/ppc64le", "app.platform=linux/ppc64le",
"app.output=type=registry", "app.output=type=registry",
}, nil) }, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -697,7 +697,7 @@ func TestReadContexts(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -713,7 +713,7 @@ func TestReadContexts(t *testing.T) {
require.Equal(t, "baz", ctxs["foo"].Path) require.Equal(t, "baz", ctxs["foo"].Path)
require.Equal(t, "def", ctxs["abc"].Path) 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, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -731,7 +731,7 @@ func TestReadContexts(t *testing.T) {
require.Equal(t, "jkl", ctxs["ghi"].Path) 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, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -766,7 +766,7 @@ func TestReadContextFromTargetUnknown(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "failed to find target bar") require.Contains(t, err.Error(), "failed to find target bar")
} }
@@ -790,7 +790,7 @@ services:
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil) m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
@@ -828,7 +828,7 @@ func TestReadContextFromTargetChain(t *testing.T) {
`), `),
} }
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(m)) require.Equal(t, 3, len(m))
@@ -867,7 +867,7 @@ func TestReadContextFromTargetInfiniteLoop(t *testing.T) {
} }
`), `),
} }
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app", "mid"}, []string{}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app", "mid"}, []string{}, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "infinite loop from") require.Contains(t, err.Error(), "infinite loop from")
} }
@@ -889,7 +889,7 @@ func TestReadContextFromTargetMultiPlatform(t *testing.T) {
} }
`), `),
} }
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
} }
@@ -910,7 +910,7 @@ func TestReadContextFromTargetInvalidPlatforms(t *testing.T) {
} }
`), `),
} }
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "defined for different platforms") require.Contains(t, err.Error(), "defined for different platforms")
} }
@@ -926,7 +926,7 @@ target "default" {
dockerfile = "test" dockerfile = "test"
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, len(g)) require.Equal(t, 0, len(g))
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -944,10 +944,10 @@ target "image" {
dockerfile = "test" dockerfile = "test"
}`)} }`)}
_, _, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil) _, _, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
m, g, err := ReadTargets(ctx, []File{f}, []string{"image"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"image"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g["default"].Targets) require.Equal(t, []string{"image"}, g["default"].Targets)
@@ -969,7 +969,7 @@ target "image" {
dockerfile = "test" dockerfile = "test"
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -995,7 +995,7 @@ target "image" {
dockerfile = "test" dockerfile = "test"
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -1003,7 +1003,7 @@ target "image" {
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["image"].Dockerfile) require.Equal(t, "test", *m["image"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil) m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -1083,7 +1083,7 @@ services:
} }
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{fhcl}, []string{"default"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fhcl}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g["default"].Targets) require.Equal(t, []string{"image"}, g["default"].Targets)
@@ -1091,7 +1091,7 @@ services:
require.Equal(t, 1, len(m["image"].Outputs)) require.Equal(t, 1, len(m["image"].Outputs))
require.Equal(t, "type=docker", m["image"].Outputs[0]) require.Equal(t, "type=docker", m["image"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image-release"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image-release"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image-release"}, g["default"].Targets) require.Equal(t, []string{"image-release"}, g["default"].Targets)
@@ -1099,7 +1099,7 @@ services:
require.Equal(t, 1, len(m["image-release"].Outputs)) require.Equal(t, 1, len(m["image-release"].Outputs))
require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0]) require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image", "image-release"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image", "image-release"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image", "image-release"}, g["default"].Targets) require.Equal(t, []string{"image", "image-release"}, g["default"].Targets)
@@ -1108,21 +1108,21 @@ services:
require.Equal(t, 1, len(m["image-release"].Outputs)) require.Equal(t, 1, len(m["image-release"].Outputs))
require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0]) require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"default"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g["default"].Targets) require.Equal(t, []string{"image"}, g["default"].Targets)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
require.Equal(t, ".", *m["image"].Context) require.Equal(t, ".", *m["image"].Context)
m, g, err = ReadTargets(ctx, []File{fjson}, []string{"default"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fjson}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g["default"].Targets) require.Equal(t, []string{"image"}, g["default"].Targets)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
require.Equal(t, ".", *m["image"].Context) require.Equal(t, ".", *m["image"].Context)
m, g, err = ReadTargets(ctx, []File{fyml}, []string{"default"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fyml}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
sort.Strings(g["default"].Targets) sort.Strings(g["default"].Targets)
@@ -1131,7 +1131,7 @@ services:
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile) require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile) require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
sort.Strings(g["default"].Targets) sort.Strings(g["default"].Targets)
@@ -1140,7 +1140,7 @@ services:
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile) require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile) require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws", "image"}, nil, nil) m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws", "image"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
sort.Strings(g["default"].Targets) sort.Strings(g["default"].Targets)
@@ -1168,7 +1168,7 @@ target "image" {
output = ["type=docker"] output = ["type=docker"]
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -1176,7 +1176,7 @@ target "image" {
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
require.Equal(t, "bar", *m["foo"].Dockerfile) require.Equal(t, "bar", *m["foo"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil) m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -1202,7 +1202,7 @@ target "image" {
output = ["type=docker"] output = ["type=docker"]
}`)} }`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo"}, g["default"].Targets) require.Equal(t, []string{"foo"}, g["default"].Targets)
@@ -1211,7 +1211,7 @@ target "image" {
require.Equal(t, "bar", *m["foo"].Dockerfile) require.Equal(t, "bar", *m["foo"].Dockerfile)
require.Equal(t, "type=docker", m["image"].Outputs[0]) require.Equal(t, "type=docker", m["image"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "image"}, nil, nil) m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "image"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(g)) require.Equal(t, 2, len(g))
require.Equal(t, []string{"foo", "image"}, g["default"].Targets) require.Equal(t, []string{"foo", "image"}, g["default"].Targets)
@@ -1273,7 +1273,7 @@ target "d" {
for _, tt := range cases { for _, tt := range cases {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, []string{"d"}, tt.overrides, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"d"}, tt.overrides, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"d"}, g["default"].Targets) require.Equal(t, []string{"d"}, g["default"].Targets)
@@ -1345,7 +1345,7 @@ group "default" {
for _, tt := range cases { for _, tt := range cases {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, tt.overrides, nil) m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, tt.overrides, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(g)) require.Equal(t, 1, len(g))
require.Equal(t, []string{"child1", "child2"}, g["default"].Targets) require.Equal(t, []string{"child1", "child2"}, g["default"].Targets)
@@ -1403,7 +1403,7 @@ func TestTargetName(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{{ _, _, err := ReadTargets(ctx, []File{{
Name: "docker-bake.hcl", Name: "docker-bake.hcl",
Data: []byte(`target "` + tt.target + `" {}`), Data: []byte(`target "` + tt.target + `" {}`),
}}, []string{tt.target}, nil, nil) }}, []string{tt.target}, nil, nil, &EntitlementConf{})
if tt.wantErr { if tt.wantErr {
require.Error(t, err) require.Error(t, err)
} else { } else {
@@ -1490,7 +1490,7 @@ target "f" {
for _, tt := range cases { for _, tt := range cases {
tt := tt tt := tt
t.Run(strings.Join(tt.names, "+"), func(t *testing.T) { t.Run(strings.Join(tt.names, "+"), func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, tt.names, nil, nil) m, g, err := ReadTargets(ctx, []File{f}, tt.names, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
var gnames []string var gnames []string
@@ -1567,7 +1567,7 @@ func TestHCLNullVars(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -1602,7 +1602,7 @@ func TestJSONNullVars(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -1677,7 +1677,7 @@ func TestAttestDuplicates(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil) m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil, &EntitlementConf{})
require.Equal(t, []string{"type=sbom,foo=bar", "type=provenance,mode=max"}, m["default"].Attest) require.Equal(t, []string{"type=sbom,foo=bar", "type=provenance,mode=max"}, m["default"].Attest)
require.NoError(t, err) require.NoError(t, err)
@@ -1688,7 +1688,7 @@ func TestAttestDuplicates(t *testing.T) {
"provenance": ptrstr("type=provenance,mode=max"), "provenance": ptrstr("type=provenance,mode=max"),
}, opts["default"].Attests) }, opts["default"].Attests)
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil) m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil, &EntitlementConf{})
require.Equal(t, []string{"type=sbom,disabled=true", "type=provenance,mode=max"}, m["default"].Attest) require.Equal(t, []string{"type=sbom,disabled=true", "type=provenance,mode=max"}, m["default"].Attest)
require.NoError(t, err) require.NoError(t, err)
@@ -1710,7 +1710,7 @@ func TestAnnotations(t *testing.T) {
}`), }`),
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -1737,7 +1737,7 @@ func TestHCLEntitlements(t *testing.T) {
}`), }`),
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -1777,7 +1777,7 @@ func TestEntitlementsForNetHostCompose(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -1808,7 +1808,7 @@ func TestEntitlementsForNetHost(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -1839,7 +1839,7 @@ func TestNetNone(t *testing.T) {
} }
ctx := context.TODO() ctx := context.TODO()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
bo, err := TargetsToBuildOpt(m, &Input{}) bo, err := TargetsToBuildOpt(m, &Input{})
@@ -1879,12 +1879,12 @@ target "app" {
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
t.Setenv("FOO", "bar") t.Setenv("FOO", "bar")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "FOO is required.") require.Contains(t, err.Error(), "FOO is required.")
}) })
@@ -1916,19 +1916,19 @@ target "app" {
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
t.Setenv("FOO", "barbar") t.Setenv("FOO", "barbar")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("InvalidLength", func(t *testing.T) { t.Run("InvalidLength", func(t *testing.T) {
t.Setenv("FOO", "bar") t.Setenv("FOO", "bar")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "FOO must be longer than 4 characters.") require.Contains(t, err.Error(), "FOO must be longer than 4 characters.")
}) })
t.Run("InvalidEmpty", func(t *testing.T) { t.Run("InvalidEmpty", func(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "FOO is required.") require.Contains(t, err.Error(), "FOO is required.")
}) })
@@ -1957,19 +1957,19 @@ target "app" {
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
t.Setenv("FOO", "bar") t.Setenv("FOO", "bar")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("SetBar", func(t *testing.T) { t.Run("SetBar", func(t *testing.T) {
t.Setenv("FOO", "bar") t.Setenv("FOO", "bar")
t.Setenv("BAR", "baz") t.Setenv("BAR", "baz")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "BAR requires FOO to be set.") require.Contains(t, err.Error(), "BAR requires FOO to be set.")
}) })
@@ -1998,12 +1998,12 @@ target "app" {
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
t.Setenv("FOO", "10") t.Setenv("FOO", "10")
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), "FOO must be greater than 5.") require.Contains(t, err.Error(), "FOO must be greater than 5.")
}) })
@@ -2025,6 +2025,23 @@ target "app" {
ctx := context.TODO() ctx := context.TODO()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
require.NoError(t, err)
}
// https://github.com/docker/buildx/issues/2858
func TestOverrideEmpty(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "app" {
output = ["./bin"]
}
`),
}
ctx := context.TODO()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.output="}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
} }

View File

@@ -19,6 +19,7 @@ import (
"github.com/docker/buildx/util/osutil" "github.com/docker/buildx/util/osutil"
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
) )
type EntitlementKey string type EntitlementKey string
@@ -113,17 +114,8 @@ func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) erro
roPaths[p] = struct{}{} roPaths[p] = struct{}{}
} }
for _, out := range bo.Exports { for _, p := range bo.ExportsLocalPathsTemporary {
if out.Type == "local" { rwPaths[p] = struct{}{}
if dest, ok := out.Attrs["dest"]; ok {
rwPaths[dest] = struct{}{}
}
}
if out.Type == "tar" {
if dest, ok := out.Attrs["dest"]; ok && dest != "-" {
rwPaths[dest] = struct{}{}
}
}
} }
for _, ce := range bo.CacheTo { for _, ce := range bo.CacheTo {
@@ -453,12 +445,20 @@ func evaluatePaths(in []string) ([]string, bool, error) {
} }
v, err := filepath.Abs(p) v, err := filepath.Abs(p)
if err != nil { if err != nil {
return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p) logrus.Warnf("failed to evaluate entitlement path %q: %v", p, err)
continue
} }
v, err = filepath.EvalSymlinks(v) v, rest, err := evaluateToExistingPath(v)
if err != nil { if err != nil {
return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p) return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p)
} }
v, err = osutil.GetLongPathName(v)
if err != nil {
return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p)
}
if rest != "" {
v = filepath.Join(v, rest)
}
out = append(out, v) out = append(out, v)
} }
return out, allowAny, nil return out, allowAny, nil
@@ -467,7 +467,7 @@ func evaluatePaths(in []string) ([]string, bool, error) {
func evaluateToExistingPaths(in map[string]struct{}) (map[string]struct{}, error) { func evaluateToExistingPaths(in map[string]struct{}) (map[string]struct{}, error) {
m := make(map[string]struct{}, len(in)) m := make(map[string]struct{}, len(in))
for p := range in { for p := range in {
v, err := evaluateToExistingPath(p) v, _, err := evaluateToExistingPath(p)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to evaluate path %q", p) return nil, errors.Wrapf(err, "failed to evaluate path %q", p)
} }
@@ -480,10 +480,10 @@ func evaluateToExistingPaths(in map[string]struct{}) (map[string]struct{}, error
return m, nil return m, nil
} }
func evaluateToExistingPath(in string) (string, error) { func evaluateToExistingPath(in string) (string, string, error) {
in, err := filepath.Abs(in) in, err := filepath.Abs(in)
if err != nil { if err != nil {
return "", err return "", "", err
} }
volLen := volumeNameLen(in) volLen := volumeNameLen(in)
@@ -538,29 +538,29 @@ func evaluateToExistingPath(in string) (string, error) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
for r := len(dest) - 1; r >= volLen; r-- { for r := len(dest) - 1; r >= volLen; r-- {
if os.IsPathSeparator(dest[r]) { if os.IsPathSeparator(dest[r]) {
return dest[:r], nil return dest[:r], in[start:], nil
} }
} }
return vol, nil return vol, in[start:], nil
} }
return "", err return "", "", err
} }
if fi.Mode()&fs.ModeSymlink == 0 { if fi.Mode()&fs.ModeSymlink == 0 {
if !fi.Mode().IsDir() && end < len(in) { if !fi.Mode().IsDir() && end < len(in) {
return "", syscall.ENOTDIR return "", "", syscall.ENOTDIR
} }
continue continue
} }
linksWalked++ linksWalked++
if linksWalked > 255 { if linksWalked > 255 {
return "", errors.New("too many symlinks") return "", "", errors.New("too many symlinks")
} }
link, err := os.Readlink(dest) link, err := os.Readlink(dest)
if err != nil { if err != nil {
return "", err return "", "", err
} }
in = link + in[end:] in = link + in[end:]
@@ -593,7 +593,7 @@ func evaluateToExistingPath(in string) (string, error) {
end = 0 end = 0
} }
} }
return filepath.Clean(dest), nil return filepath.Clean(dest), "", nil
} }
func volumeNameLen(s string) int { func volumeNameLen(s string) int {

View File

@@ -10,7 +10,6 @@ import (
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/controller/pb" "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/osutil" "github.com/docker/buildx/util/osutil"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -90,7 +89,7 @@ func TestEvaluateToExistingPath(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := evaluateToExistingPath(tt.input) result, _, err := evaluateToExistingPath(tt.input)
if tt.expectErr { if tt.expectErr {
require.Error(t, err) require.Error(t, err)
@@ -279,25 +278,10 @@ func TestValidateEntitlements(t *testing.T) {
{ {
name: "ExportLocal", name: "ExportLocal",
opt: build.Options{ opt: build.Options{
Exports: []client.ExportEntry{ ExportsLocalPathsTemporary: []string{
{ dir1,
Type: "local", filepath.Join(dir1, "subdir"),
Attrs: map[string]string{ dir2,
"dest": dir1,
},
},
{
Type: "local",
Attrs: map[string]string{
"dest": filepath.Join(dir1, "subdir"),
},
},
{
Type: "local",
Attrs: map[string]string{
"dest": dir2,
},
},
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{
@@ -357,7 +341,7 @@ func TestValidateEntitlements(t *testing.T) {
return nil return nil
} }
// if not, then escapeLink is not allowed // if not, then escapeLink is not allowed
exp, err := evaluateToExistingPath(escapeLink) exp, _, err := evaluateToExistingPath(escapeLink)
require.NoError(t, err) require.NoError(t, err)
exp, err = filepath.EvalSymlinks(exp) exp, err = filepath.EvalSymlinks(exp)
require.NoError(t, err) require.NoError(t, err)
@@ -379,6 +363,48 @@ func TestValidateEntitlements(t *testing.T) {
}, },
expected: EntitlementConf{}, expected: EntitlementConf{},
}, },
{
name: "NonExistingAllowedPathSubpath",
opt: build.Options{
ExportsLocalPathsTemporary: []string{
dir1,
},
},
conf: EntitlementConf{
FSRead: []string{wd},
FSWrite: []string{filepath.Join(dir1, "not/exists")},
},
expected: EntitlementConf{
FSWrite: []string{expDir1}, // dir1 is still needed as only subpath was allowed
},
},
{
name: "NonExistingAllowedPathMatches",
opt: build.Options{
ExportsLocalPathsTemporary: []string{
filepath.Join(dir1, "not/exists"),
},
},
conf: EntitlementConf{
FSRead: []string{wd},
FSWrite: []string{filepath.Join(dir1, "not/exists")},
},
expected: EntitlementConf{
FSWrite: []string{expDir1}, // dir1 is still needed as build also needs to write not/exists directory
},
},
{
name: "NonExistingBuildPath",
opt: build.Options{
ExportsLocalPathsTemporary: []string{
filepath.Join(dir1, "not/exists"),
},
},
conf: EntitlementConf{
FSRead: []string{wd},
FSWrite: []string{dir1},
},
},
} }
for _, tc := range tcases { for _, tc := range tcases {

View File

@@ -70,6 +70,7 @@ type Options struct {
CacheTo []client.CacheOptionsEntry CacheTo []client.CacheOptionsEntry
CgroupParent string CgroupParent string
Exports []client.ExportEntry Exports []client.ExportEntry
ExportsLocalPathsTemporary []string // should be removed after client.ExportEntry update in buildkit v0.19.0
ExtraHosts []string ExtraHosts []string
Labels map[string]string Labels map[string]string
NetworkMode string NetworkMode string

View File

@@ -199,7 +199,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
} }
} }
tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults) tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults, &ent)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -93,7 +93,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.Buil
} }
opts.Session = append(opts.Session, ssh) opts.Session = append(opts.Session, ssh)
outputs, err := controllerapi.CreateExports(in.Exports) outputs, _, err := controllerapi.CreateExports(in.Exports)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }

View File

@@ -10,15 +10,16 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) { func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, error) {
var outs []client.ExportEntry var outs []client.ExportEntry
var localPaths []string
if len(entries) == 0 { if len(entries) == 0 {
return nil, nil return nil, nil, nil
} }
var stdoutUsed bool var stdoutUsed bool
for _, entry := range entries { for _, entry := range entries {
if entry.Type == "" { if entry.Type == "" {
return nil, errors.Errorf("type is required for output") return nil, nil, errors.Errorf("type is required for output")
} }
out := client.ExportEntry{ out := client.ExportEntry{
@@ -49,20 +50,21 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
if supportDir { if supportDir {
if entry.Destination == "" { if entry.Destination == "" {
return nil, errors.Errorf("dest is required for %s exporter", out.Type) return nil, nil, errors.Errorf("dest is required for %s exporter", out.Type)
} }
if entry.Destination == "-" { if entry.Destination == "-" {
return nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type) return nil, nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
} }
fi, err := os.Stat(entry.Destination) fi, err := os.Stat(entry.Destination)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination) return nil, nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
} }
if err == nil && !fi.IsDir() { if err == nil && !fi.IsDir() {
return nil, errors.Errorf("destination directory %s is a file", entry.Destination) return nil, nil, errors.Errorf("destination directory %s is a file", entry.Destination)
} }
out.OutputDir = entry.Destination out.OutputDir = entry.Destination
localPaths = append(localPaths, entry.Destination)
} }
if supportFile { if supportFile {
if entry.Destination == "" && out.Type != client.ExporterDocker { if entry.Destination == "" && out.Type != client.ExporterDocker {
@@ -70,32 +72,33 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
} }
if entry.Destination == "-" { if entry.Destination == "-" {
if stdoutUsed { if stdoutUsed {
return nil, errors.Errorf("multiple outputs configured to write to stdout") return nil, nil, errors.Errorf("multiple outputs configured to write to stdout")
} }
if _, err := console.ConsoleFromFile(os.Stdout); err == nil { if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
return nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type) return nil, nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
} }
out.Output = wrapWriteCloser(os.Stdout) out.Output = wrapWriteCloser(os.Stdout)
stdoutUsed = true stdoutUsed = true
} else if entry.Destination != "" { } else if entry.Destination != "" {
fi, err := os.Stat(entry.Destination) fi, err := os.Stat(entry.Destination)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination) return nil, nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
} }
if err == nil && fi.IsDir() { if err == nil && fi.IsDir() {
return nil, errors.Errorf("destination file %s is a directory", entry.Destination) return nil, nil, errors.Errorf("destination file %s is a directory", entry.Destination)
} }
f, err := os.Create(entry.Destination) f, err := os.Create(entry.Destination)
if err != nil { if err != nil {
return nil, errors.Errorf("failed to open %s", err) return nil, nil, errors.Errorf("failed to open %s", err)
} }
out.Output = wrapWriteCloser(f) out.Output = wrapWriteCloser(f)
localPaths = append(localPaths, entry.Destination)
} }
} }
outs = append(outs, out) outs = append(outs, out)
} }
return outs, nil return outs, localPaths, nil
} }
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) { func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"testing" "testing"
@@ -46,6 +47,14 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakeRemoteDockerfileCwd, testBakeRemoteDockerfileCwd,
testBakeRemoteLocalContextRemoteDockerfile, testBakeRemoteLocalContextRemoteDockerfile,
testBakeEmpty, testBakeEmpty,
testBakeSetNonExistingSubdirNoParallel,
testBakeSetNonExistingOutsideNoParallel,
testBakeSetExistingOutsideNoParallel,
testBakeDefinitionNotExistingSubdirNoParallel,
testBakeDefinitionNotExistingOutsideNoParallel,
testBakeDefinitionExistingOutsideNoParallel,
testBakeDefinitionSymlinkOutsideNoParallel,
testBakeDefinitionSymlinkOutsideGrantedNoParallel,
testBakeShmSize, testBakeShmSize,
testBakeUlimits, testBakeUlimits,
testBakeMetadataProvenance, testBakeMetadataProvenance,
@@ -705,6 +714,261 @@ target "default" {
require.Contains(t, string(dt), `size=131072k`) require.Contains(t, string(dt), `size=131072k`)
} }
func testBakeSetNonExistingSubdirNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain", "--set", "*.output=type=local,dest="+filepath.Join(dir, "not/exists")))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
require.Contains(t, string(out), `#1 [internal] load local bake definitions`)
require.Contains(t, string(out), `#1 reading docker-bake.hcl`)
require.FileExists(t, filepath.Join(dir, "not/exists/foo"))
})
}
}
func testBakeSetNonExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
destDir := t.TempDir()
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain", "--set", "*.output=type=local,dest="+filepath.Join(destDir, "not/exists")))
out, err := cmd.CombinedOutput()
if ent {
require.Error(t, err, string(out))
require.Contains(t, string(out), "ERROR: additional privileges requested")
} else {
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "not/exists/foo"))
}
})
}
}
func testBakeSetExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
destDir := t.TempDir()
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain", "--set", "*.output=type=local,dest="+destDir))
out, err := cmd.CombinedOutput()
// existing directory via --set is always allowed
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "foo"))
})
}
}
func testBakeDefinitionNotExistingSubdirNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
output = ["type=local,dest=not/exists"]
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain"))
out, err := cmd.CombinedOutput()
// subdirs of working directory are always allowed
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(dir, "not/exists/foo"))
})
}
}
func testBakeDefinitionNotExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
destDir := t.TempDir()
bakefile := []byte(fmt.Sprintf(`
target "default" {
output = ["type=local,dest=%s/not/exists"]
}
`, destDir))
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain"))
out, err := cmd.CombinedOutput()
if ent {
require.Error(t, err, string(out))
require.Contains(t, string(out), "ERROR: additional privileges requested")
} else {
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "not/exists/foo"))
}
})
}
}
func testBakeDefinitionExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
destDir := t.TempDir()
bakefile := []byte(fmt.Sprintf(`
target "default" {
output = ["type=local,dest=%s"]
}
`, destDir))
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain"))
out, err := cmd.CombinedOutput()
if ent {
require.Error(t, err, string(out))
require.Contains(t, string(out), "ERROR: additional privileges requested")
} else {
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "foo"))
}
})
}
}
func testBakeDefinitionSymlinkOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
destDir := t.TempDir()
bakefile := []byte(`
target "default" {
output = ["type=local,dest=out"]
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
fstest.Symlink(destDir, "out"),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain"))
out, err := cmd.CombinedOutput()
if ent {
require.Error(t, err, string(out))
require.Contains(t, string(out), "ERROR: additional privileges requested")
} else {
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "foo"))
}
})
}
}
func testBakeDefinitionSymlinkOutsideGrantedNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
t.Setenv("BUILDX_BAKE_ENTITLEMENTS_FS", strconv.FormatBool(ent))
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
destDir := t.TempDir()
bakefile := []byte(`
target "default" {
output = ["type=local,dest=out"]
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
fstest.Symlink(destDir, "out"),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain", "--allow", "fs.write="+destDir))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
require.FileExists(t, filepath.Join(destDir, "foo"))
})
}
}
func testBakeUlimits(t *testing.T, sb integration.Sandbox) { func testBakeUlimits(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(` dockerfile := []byte(`
FROM busybox AS build FROM busybox AS build

View File

@@ -14,6 +14,9 @@ import (
func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) { func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) {
outs := make([]*controllerapi.CacheOptionsEntry, 0, len(in)) outs := make([]*controllerapi.CacheOptionsEntry, 0, len(in))
for _, in := range in { for _, in := range in {
if in == "" {
continue
}
fields, err := csvvalue.Fields(in, nil) fields, err := csvvalue.Fields(in, nil)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -19,6 +19,9 @@ func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
return nil, nil return nil, nil
} }
for _, s := range inp { for _, s := range inp {
if s == "" {
continue
}
fields, err := csvvalue.Fields(s, nil) fields, err := csvvalue.Fields(s, nil)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -11,6 +11,9 @@ import (
func ParseSecretSpecs(sl []string) ([]*controllerapi.Secret, error) { func ParseSecretSpecs(sl []string) ([]*controllerapi.Secret, error) {
fs := make([]*controllerapi.Secret, 0, len(sl)) fs := make([]*controllerapi.Secret, 0, len(sl))
for _, v := range sl { for _, v := range sl {
if v == "" {
continue
}
s, err := parseSecret(v) s, err := parseSecret(v)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -14,6 +14,9 @@ func ParseSSHSpecs(sl []string) ([]*controllerapi.SSH, error) {
} }
for _, s := range sl { for _, s := range sl {
if s == "" {
continue
}
parts := strings.SplitN(s, "=", 2) parts := strings.SplitN(s, "=", 2)
out := controllerapi.SSH{ out := controllerapi.SSH{
ID: parts[0], ID: parts[0],