policy: implement policy logging via progress printer

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-01-14 09:10:34 -08:00
parent 5ad09ce3eb
commit be42b48462
8 changed files with 188 additions and 60 deletions
+4 -4
View File
@@ -132,7 +132,7 @@ type policyOpt struct {
Files []policy.File
FS func() (fs.StatFS, func() error, error)
Strict bool
LogLevel logrus.Level
LogLevel *logrus.Level
}
func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policyOpt, error) {
@@ -177,7 +177,7 @@ func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policy
last.Strict = *cfg.Strict
}
if cfg.LogLevel != nil {
last.LogLevel = *cfg.LogLevel
last.LogLevel = cfg.LogLevel
}
}
continue
@@ -190,13 +190,13 @@ func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policy
opt.Strict = *last.Strict
}
if last.LogLevel != nil {
opt.LogLevel = *last.LogLevel
opt.LogLevel = last.LogLevel
}
if cfg.Strict != nil {
opt.Strict = *cfg.Strict
}
if cfg.LogLevel != nil {
opt.LogLevel = *cfg.LogLevel
opt.LogLevel = cfg.LogLevel
}
opt.FS = defaultPolicy.FS
out = append(out, opt)
+106 -6
View File
@@ -3,9 +3,9 @@ package build
import (
"bytes"
"context"
"fmt"
"io"
"io/fs"
"log"
"maps"
"os"
"path"
@@ -15,6 +15,7 @@ import (
"strings"
"sync"
"syscall"
"time"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/containerd/console"
@@ -48,6 +49,7 @@ import (
"github.com/moby/buildkit/util/gitutil"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/tonistiigi/fsutil"
)
@@ -60,6 +62,79 @@ var sendGitQueryAsInput = sync.OnceValue(func() bool {
return false
})
type policyProgressLogger struct {
ch chan *client.SolveStatus
done chan struct{}
dgst digest.Digest
started time.Time
name string
}
func newPolicyProgressLogger(pw progress.Writer, name string) *policyProgressLogger {
if pw == nil {
return nil
}
ch, done := progress.NewChannel(pw)
dgst := digest.FromBytes([]byte(identity.NewID()))
tm := time.Now()
vtx := client.Vertex{
Digest: dgst,
Name: name,
Started: &tm,
}
ch <- &client.SolveStatus{Vertexes: []*client.Vertex{&vtx}}
return &policyProgressLogger{
ch: ch,
done: done,
dgst: dgst,
started: tm,
name: name,
}
}
func (l *policyProgressLogger) Log(msg string) {
if l == nil || msg == "" {
return
}
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
l.ch <- &client.SolveStatus{
Logs: []*client.VertexLog{{
Vertex: l.dgst,
Stream: 1,
Data: []byte(msg),
Timestamp: time.Now(),
}},
}
}
func (l *policyProgressLogger) Write(p []byte) (int, error) {
if len(p) > 0 {
l.Log(string(p))
}
return len(p), nil
}
func (l *policyProgressLogger) Close(err error) {
if l == nil {
return
}
tm := time.Now()
vtx := client.Vertex{
Digest: l.dgst,
Name: l.name,
Started: &l.started,
Completed: &tm,
}
if err != nil {
vtx.Error = err.Error()
}
l.ch <- &client.SolveStatus{Vertexes: []*client.Vertex{&vtx}}
close(l.ch)
<-l.done
}
func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *Options, bopts gateway.BuildOpts, cfg *confutil.Config, pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
nodeDriver := node.Driver
defers := make([]func(), 0, 2)
@@ -347,14 +422,39 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
if err != nil {
return nil, nil, err
}
var policyFiles []string
for _, popt := range popts {
for _, f := range popt.Files {
if f.Filename != "" {
policyFiles = append(policyFiles, f.Filename)
}
}
}
var policyLogger *policyProgressLogger
if len(policyFiles) > 0 {
policyLogger = newPolicyProgressLogger(pw, fmt.Sprintf("loading policies %s", strings.Join(policyFiles, ", ")))
}
if policyLogger != nil {
defers = append(defers, func() {
policyLogger.Close(nil)
})
}
var cbs []policysession.PolicyCallback
for _, popt := range popts {
policyLevel := logrus.GetLevel()
if popt.LogLevel != nil {
policyLevel = *popt.LogLevel
}
logf := func(level logrus.Level, msg string) {
if policyLogger == nil || level > policyLevel {
return
}
policyLogger.Log(msg)
}
p := policy.NewPolicy(policy.Opt{
Files: popt.Files,
Env: env,
Log: func(msg string) {
log.Printf("[policy] %s", msg)
},
Files: popt.Files,
Env: env,
Log: logf,
FS: opt.Inputs.policy.FS,
VerifierProvider: policy.SignatureVerifier(cfg),
})
+8 -7
View File
@@ -33,7 +33,7 @@ func TestWithPolicyConfigDefaults(t *testing.T) {
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.Nil(t, out[0].LogLevel)
require.NotNil(t, out[0].FS)
}
@@ -54,11 +54,10 @@ func TestWithPolicyConfigDisabled(t *testing.T) {
})
require.Error(t, err)
out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true, LogLevel: levelPtr(logrus.WarnLevel)},
})
require.NoError(t, err)
require.Nil(t, out)
require.Error(t, err)
_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true},
@@ -66,7 +65,7 @@ func TestWithPolicyConfigDisabled(t *testing.T) {
})
require.Error(t, err)
out, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
{Disabled: true},
})
require.NoError(t, err)
@@ -104,7 +103,8 @@ func TestWithPolicyConfigStrictAndLogLevel(t *testing.T) {
require.NoError(t, err)
require.Len(t, out, 1)
require.True(t, out[0].Strict)
require.Equal(t, logrus.WarnLevel, out[0].LogLevel)
require.NotNil(t, out[0].LogLevel)
require.Equal(t, logrus.WarnLevel, *out[0].LogLevel)
}
// TestWithPolicyConfigStrictIgnoredWithoutPolicy ensures strict without any policy produces no entries.
@@ -135,7 +135,8 @@ func TestWithPolicyConfigMultipleFilesAndOverrides(t *testing.T) {
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.NotNil(t, out[1].LogLevel)
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)