build: add --policy flag for controlling policy configuration

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-01-14 09:09:08 -08:00
parent d0e9e8cab5
commit 4af8cdb6af
3 changed files with 296 additions and 2 deletions
+83 -2
View File
@@ -97,6 +97,15 @@ type Options struct {
SourcePolicy *spb.Policy
GroupRef string
Annotations map[exptypes.AnnotationKey]string // Not used during build, annotations are already set in Exports. Just used to check for support with drivers.
Policy []PolicyConfig
}
type PolicyConfig struct {
Files []policy.File
Reset bool
Disabled bool
Strict *bool
LogLevel *logrus.Level
}
type CallFunc struct {
@@ -120,8 +129,80 @@ type Inputs struct {
}
type policyOpt struct {
Files []policy.File
FS func() (fs.StatFS, func() error, error)
Files []policy.File
FS func() (fs.StatFS, func() error, error)
Strict bool
LogLevel logrus.Level
}
func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policyOpt, error) {
if len(configs) == 0 {
if len(defaultPolicy.Files) == 0 {
return nil, nil
}
return []policyOpt{defaultPolicy}, nil
}
for _, cfg := range configs {
if !cfg.Disabled {
continue
}
if cfg.Reset || cfg.Strict != nil || cfg.LogLevel != nil || len(cfg.Files) > 0 {
return nil, errors.New("disabled policy cannot be combined with other policy flags")
}
if len(configs) > 1 {
return nil, errors.New("disabled policy cannot be combined with other policy flags")
}
return nil, nil
}
out := make([]policyOpt, 0, len(configs)+1)
if len(defaultPolicy.Files) != 0 {
out = append(out, defaultPolicy)
}
var last PolicyConfig
for _, cfg := range configs {
if cfg.Reset {
out = nil
}
if len(cfg.Files) == 0 {
if len(out) == 0 {
last = cfg
} else {
last := &out[len(out)-1]
if cfg.Strict != nil {
last.Strict = *cfg.Strict
}
if cfg.LogLevel != nil {
last.LogLevel = *cfg.LogLevel
}
}
continue
}
opt := policyOpt{
Files: cfg.Files,
}
if last.Strict != nil {
opt.Strict = *last.Strict
}
if last.LogLevel != nil {
opt.LogLevel = *last.LogLevel
}
if cfg.Strict != nil {
opt.Strict = *cfg.Strict
}
if cfg.LogLevel != nil {
opt.LogLevel = *cfg.LogLevel
}
opt.FS = defaultPolicy.FS
out = append(out, opt)
}
return out, nil
}
type NamedContext struct {
+143
View File
@@ -0,0 +1,143 @@
package build
import (
"io/fs"
"testing"
"github.com/docker/buildx/policy"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)
func boolPtr(v bool) *bool {
return &v
}
func levelPtr(v logrus.Level) *logrus.Level {
return &v
}
// TestWithPolicyConfigDefaults ensures default policy is returned when no configs are provided.
func TestWithPolicyConfigDefaults(t *testing.T) {
defaultPolicy := policyOpt{
Files: []policy.File{
{Filename: "default.rego", Data: []byte("package policy")},
},
FS: func() (fs.StatFS, func() error, error) {
return nil, nil, nil
},
}
out, err := withPolicyConfig(defaultPolicy, nil)
require.NoError(t, err)
require.Len(t, out, 1)
require.Equal(t, defaultPolicy.Files, out[0].Files)
require.False(t, out[0].Strict)
require.Equal(t, logrus.Level(0), out[0].LogLevel)
require.NotNil(t, out[0].FS)
}
// TestWithPolicyConfigDisabled validates disabled policy behavior across invalid and valid combinations.
func TestWithPolicyConfigDisabled(t *testing.T) {
_, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true, Files: []policy.File{{Filename: "x.rego"}}},
})
require.Error(t, err)
_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true, Reset: true},
})
require.Error(t, err)
_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true, Strict: boolPtr(true)},
})
require.Error(t, err)
out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true, LogLevel: levelPtr(logrus.WarnLevel)},
})
require.NoError(t, err)
require.Nil(t, out)
_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true},
{},
})
require.Error(t, err)
out, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true},
})
require.NoError(t, err)
require.Nil(t, out)
}
// TestWithPolicyConfigResetAndFiles ensures reset drops defaults and uses explicitly provided files.
func TestWithPolicyConfigResetAndFiles(t *testing.T) {
defaultPolicy := policyOpt{
Files: []policy.File{{Filename: "default.rego"}},
FS: func() (fs.StatFS, func() error, error) {
return nil, nil, nil
},
}
out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
{Reset: true},
{Files: []policy.File{{Filename: "a.rego"}}},
})
require.NoError(t, err)
require.Len(t, out, 1)
require.Equal(t, "a.rego", out[0].Files[0].Filename)
require.NotNil(t, out[0].FS)
}
// TestWithPolicyConfigStrictAndLogLevel ensures strict and log level apply to existing policy.
func TestWithPolicyConfigStrictAndLogLevel(t *testing.T) {
defaultPolicy := policyOpt{
Files: []policy.File{{Filename: "default.rego"}},
}
out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
{Strict: boolPtr(true), LogLevel: levelPtr(logrus.WarnLevel)},
})
require.NoError(t, err)
require.Len(t, out, 1)
require.True(t, out[0].Strict)
require.Equal(t, logrus.WarnLevel, out[0].LogLevel)
}
// TestWithPolicyConfigStrictIgnoredWithoutPolicy ensures strict without any policy produces no entries.
func TestWithPolicyConfigStrictIgnoredWithoutPolicy(t *testing.T) {
out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
{Strict: boolPtr(true)},
})
require.NoError(t, err)
require.Len(t, out, 0)
}
// TestWithPolicyConfigMultipleFilesAndOverrides ensures per-entry overrides and carryover apply across multiple files.
func TestWithPolicyConfigMultipleFilesAndOverrides(t *testing.T) {
defaultPolicy := policyOpt{
Files: []policy.File{{Filename: "default.rego"}},
FS: func() (fs.StatFS, func() error, error) {
return nil, nil, nil
},
}
out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
{Files: []policy.File{{Filename: "a.rego"}}},
{Strict: boolPtr(true), LogLevel: levelPtr(logrus.WarnLevel)},
{Files: []policy.File{{Filename: "b.rego"}}, Strict: boolPtr(true)},
})
require.NoError(t, err)
require.Len(t, out, 3)
require.Equal(t, "default.rego", out[0].Files[0].Filename)
require.Equal(t, "a.rego", out[1].Files[0].Filename)
require.True(t, out[1].Strict)
require.Equal(t, logrus.WarnLevel, out[1].LogLevel)
require.Equal(t, "b.rego", out[2].Files[0].Filename)
require.True(t, out[2].Strict)
require.NotNil(t, out[1].FS)
require.NotNil(t, out[2].FS)
}