mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 09:17:49 +08:00
history: add local filters for older buildkit versions
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
900502b139
commit
f1b895196c
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/localstate"
|
"github.com/docker/buildx/localstate"
|
||||||
controlapi "github.com/moby/buildkit/api/services/control"
|
controlapi "github.com/moby/buildkit/api/services/control"
|
||||||
|
"github.com/moby/buildkit/util/gitutil"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
@ -149,8 +150,9 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var matchers []matchFunc
|
||||||
if len(filters) > 0 {
|
if len(filters) > 0 {
|
||||||
filters, err = dockerFiltersToBuildkit(filters)
|
filters, matchers, err = dockerFiltersToBuildkit(filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -183,6 +185,7 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
|||||||
ts = &t
|
ts = &t
|
||||||
}
|
}
|
||||||
defer serv.CloseSend()
|
defer serv.CloseSend()
|
||||||
|
loop0:
|
||||||
for {
|
for {
|
||||||
he, err := serv.Recv()
|
he, err := serv.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -198,6 +201,13 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for older buildkit that don't support filters apply local filters
|
||||||
|
for _, matcher := range matchers {
|
||||||
|
if !matcher(he.Record) {
|
||||||
|
continue loop0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
records = append(records, historyRecord{
|
records = append(records, historyRecord{
|
||||||
BuildHistoryRecord: he.Record,
|
BuildHistoryRecord: he.Record,
|
||||||
currentTimestamp: ts,
|
currentTimestamp: ts,
|
||||||
@ -245,18 +255,22 @@ func formatDuration(d time.Duration) string {
|
|||||||
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
|
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dockerFiltersToBuildkit(in []string) ([]string, error) {
|
type matchFunc func(*controlapi.BuildHistoryRecord) bool
|
||||||
|
|
||||||
|
func dockerFiltersToBuildkit(in []string) ([]string, []matchFunc, error) {
|
||||||
out := []string{}
|
out := []string{}
|
||||||
|
matchers := []matchFunc{}
|
||||||
for _, f := range in {
|
for _, f := range in {
|
||||||
key, value, sep, found := multiCut(f, "=", "!=", "<=", "<", ">=", ">")
|
key, value, sep, found := cutAny(f, "!=", "=", "<=", "<", ">=", ">")
|
||||||
if !found {
|
if !found {
|
||||||
return nil, errors.Errorf("invalid filter %q", f)
|
return nil, nil, errors.Errorf("invalid filter %q", f)
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
case "ref", "repository", "status":
|
case "ref", "repository", "status":
|
||||||
if sep != "=" && sep != "!=" {
|
if sep != "=" && sep != "!=" {
|
||||||
return nil, errors.Errorf("invalid separator for %q, expected = or !=", f)
|
return nil, nil, errors.Errorf("invalid separator for %q, expected = or !=", f)
|
||||||
}
|
}
|
||||||
|
matchers = append(matchers, valueFiler(key, value, sep))
|
||||||
if sep == "=" {
|
if sep == "=" {
|
||||||
if key == "status" {
|
if key == "status" {
|
||||||
sep = "=="
|
sep = "=="
|
||||||
@ -264,17 +278,122 @@ func dockerFiltersToBuildkit(in []string) ([]string, error) {
|
|||||||
sep = "~="
|
sep = "~="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "createdAt", "completedAt", "duration":
|
case "startedAt", "completedAt", "duration":
|
||||||
if sep == "=" || sep == "!=" {
|
if sep == "=" || sep == "!=" {
|
||||||
return nil, errors.Errorf("invalid separator for %q, expected <=, <, >= or >", f)
|
return nil, nil, errors.Errorf("invalid separator for %q, expected <=, <, >= or >", f)
|
||||||
}
|
}
|
||||||
|
matcher, err := timeBasedFilter(key, value, sep)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
default:
|
||||||
|
return nil, nil, errors.Errorf("unsupported filter %q", f)
|
||||||
}
|
}
|
||||||
out = append(out, key+sep+value)
|
out = append(out, key+sep+value)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, matchers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiCut(s string, seps ...string) (before, after, sep string, found bool) {
|
func valueFiler(key, value, sep string) matchFunc {
|
||||||
|
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||||
|
var recValue string
|
||||||
|
switch key {
|
||||||
|
case "ref":
|
||||||
|
recValue = rec.Ref
|
||||||
|
case "repository":
|
||||||
|
v, ok := rec.FrontendAttrs["vcs:source"]
|
||||||
|
if ok {
|
||||||
|
recValue = v
|
||||||
|
} else {
|
||||||
|
if context, ok := rec.FrontendAttrs["context"]; ok {
|
||||||
|
if ref, err := gitutil.ParseGitRef(context); err == nil {
|
||||||
|
recValue = ref.Remote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "status":
|
||||||
|
if rec.CompletedAt != nil {
|
||||||
|
if rec.Error != nil {
|
||||||
|
if strings.Contains(rec.Error.Message, "context canceled") {
|
||||||
|
recValue = "canceled"
|
||||||
|
} else {
|
||||||
|
recValue = "error"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
recValue = "completed"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
recValue = "running"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch sep {
|
||||||
|
case "=":
|
||||||
|
if key == "status" {
|
||||||
|
return recValue == value
|
||||||
|
}
|
||||||
|
return strings.Contains(recValue, value)
|
||||||
|
case "!=":
|
||||||
|
return recValue != value
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeBasedFilter(key, value, sep string) (matchFunc, error) {
|
||||||
|
var cmp int64
|
||||||
|
switch key {
|
||||||
|
case "startedAt", "completedAt":
|
||||||
|
v, err := time.ParseDuration(value)
|
||||||
|
if err == nil {
|
||||||
|
tm := time.Now().Add(-v)
|
||||||
|
cmp = tm.Unix()
|
||||||
|
} else {
|
||||||
|
tm, err := time.Parse(time.RFC3339, value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("invalid time %s", value)
|
||||||
|
}
|
||||||
|
cmp = tm.Unix()
|
||||||
|
}
|
||||||
|
case "duration":
|
||||||
|
v, err := time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("invalid duration %s", value)
|
||||||
|
}
|
||||||
|
cmp = int64(v)
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||||
|
var val int64
|
||||||
|
switch key {
|
||||||
|
case "startedAt":
|
||||||
|
val = rec.CreatedAt.AsTime().Unix()
|
||||||
|
case "completedAt":
|
||||||
|
if rec.CompletedAt != nil {
|
||||||
|
val = rec.CompletedAt.AsTime().Unix()
|
||||||
|
}
|
||||||
|
case "duration":
|
||||||
|
if rec.CompletedAt != nil {
|
||||||
|
val = int64(rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch sep {
|
||||||
|
case ">=":
|
||||||
|
return val >= cmp
|
||||||
|
case "<=":
|
||||||
|
return val <= cmp
|
||||||
|
case ">":
|
||||||
|
return val > cmp
|
||||||
|
default:
|
||||||
|
return val < cmp
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cutAny(s string, seps ...string) (before, after, sep string, found bool) {
|
||||||
for _, sep := range seps {
|
for _, sep := range seps {
|
||||||
if idx := strings.Index(s, sep); idx != -1 {
|
if idx := strings.Index(s, sep); idx != -1 {
|
||||||
return s[:idx], s[idx+len(sep):], sep, true
|
return s[:idx], s[idx+len(sep):], sep, true
|
||||||
|
@ -6,10 +6,12 @@ List build records
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
|:----------------|:--------------|:--------|:---------------------------------------------|
|
||||||
| `--builder` | `string` | | Override the configured builder instance |
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||||
|
| `--filter` | `stringArray` | | Provide filter values (e.g., `status=error`) |
|
||||||
| `--format` | `string` | `table` | Format the output |
|
| `--format` | `string` | `table` | Format the output |
|
||||||
|
| `--local` | `bool` | | List records for current repository only |
|
||||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user