From 80f20e2d450e5a54883229deb47d2ee19f8a03a1 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 20 May 2026 14:00:42 +0200 Subject: [PATCH 1/5] Add admin `tasks` command. --- cmd/admin/v2/commands.go | 3 +- cmd/admin/v2/task.go | 125 +++++++++++++++++++ cmd/sorters/task.go | 35 ++++++ cmd/tableprinters/common.go | 7 ++ cmd/tableprinters/task.go | 73 +++++++++++ tests/e2e/admin/task_test.go | 203 +++++++++++++++++++++++++++++++ tests/e2e/testresources/tasks.go | 68 +++++++++++ 7 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 cmd/admin/v2/task.go create mode 100644 cmd/sorters/task.go create mode 100644 cmd/tableprinters/task.go create mode 100644 tests/e2e/admin/task_test.go create mode 100644 tests/e2e/testresources/tasks.go diff --git a/cmd/admin/v2/commands.go b/cmd/admin/v2/commands.go index ee44ba2..70667cb 100644 --- a/cmd/admin/v2/commands.go +++ b/cmd/admin/v2/commands.go @@ -9,7 +9,7 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { adminCmd := &cobra.Command{ Use: "admin", Short: "admin commands", - Long: "", + Long: "these commands utilize the admin api, which can only be accessed by metal-stack operators.", SilenceUsage: true, Hidden: true, } @@ -19,6 +19,7 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { adminCmd.AddCommand(newImageCmd(c)) adminCmd.AddCommand(newProjectCmd(c)) adminCmd.AddCommand(newSwitchCmd(c)) + adminCmd.AddCommand(newTaskCmd(c)) adminCmd.AddCommand(newTenantCmd(c)) adminCmd.AddCommand(newTokenCmd(c)) diff --git a/cmd/admin/v2/task.go b/cmd/admin/v2/task.go new file mode 100644 index 0000000..696491d --- /dev/null +++ b/cmd/admin/v2/task.go @@ -0,0 +1,125 @@ +package v2 + +import ( + "fmt" + + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type task struct { + c *config.Config +} + +func newTaskCmd(c *config.Config) *cobra.Command { + w := &task{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[any, any, *adminv2.TaskInfo]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "task", + Plural: "tasks", + Description: "get task insights", + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + Sorter: sorters.TaskSorter(), + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("queue", "default", "the queue for which tasks should be described") + }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("queue", "", "the queue for which tasks should be listed") + }, + DeleteCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("queue", "default", "the queue of the task which should be delete") + }, + OnlyCmds: genericcli.OnlyCmds(genericcli.ListCmd, genericcli.DescribeCmd, genericcli.DeleteCmd), + } + + queueCmd := &cobra.Command{ + Use: "queues", + Short: "list all queues", + RunE: func(cmd *cobra.Command, args []string) error { + return w.queues() + }, + } + + return genericcli.NewCmds(cmdsConfig, queueCmd) +} + +func (t *task) queues() error { + ctx, cancel := t.c.NewRequestContext() + defer cancel() + + req := &adminv2.TaskServiceQueuesRequest{} + + resp, err := t.c.Client.Adminv2().Task().Queues(ctx, req) + if err != nil { + return fmt.Errorf("failed to get task queues: %w", err) + } + + return t.c.ListPrinter.Print(resp) +} + +func (t *task) Get(id string) (*adminv2.TaskInfo, error) { + ctx, cancel := t.c.NewRequestContext() + defer cancel() + + req := &adminv2.TaskServiceGetRequest{TaskId: id, Queue: viper.GetString("queue")} + + resp, err := t.c.Client.Adminv2().Task().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get task: %w", err) + } + + return resp.Task, nil +} + +func (t *task) List() ([]*adminv2.TaskInfo, error) { + ctx, cancel := t.c.NewRequestContext() + defer cancel() + + req := &adminv2.TaskServiceListRequest{ + Queue: pointer.PointerOrNil(viper.GetString("queue")), + } + + resp, err := t.c.Client.Adminv2().Task().List(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to list tasks: %w", err) + } + + return resp.Tasks, nil +} + +func (t *task) Delete(id string) (*adminv2.TaskInfo, error) { + ctx, cancel := t.c.NewRequestContext() + defer cancel() + + req := &adminv2.TaskServiceDeleteRequest{TaskId: id, Queue: viper.GetString("queue")} + + _, err := t.c.Client.Adminv2().Task().Delete(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to delete task: %w", err) + } + + return nil, nil +} + +func (t *task) Create(rq any) (*adminv2.TaskInfo, error) { + panic("unimplemented") +} + +func (t *task) Convert(r *adminv2.TaskInfo) (string, any, any, error) { + panic("unimplemented") +} + +func (t *task) Update(rq any) (*adminv2.TaskInfo, error) { + panic("unimplemented") +} diff --git a/cmd/sorters/task.go b/cmd/sorters/task.go new file mode 100644 index 0000000..4a15a47 --- /dev/null +++ b/cmd/sorters/task.go @@ -0,0 +1,35 @@ +package sorters + +import ( + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/metal-stack/metal-lib/pkg/multisort" +) + +func TaskSorter() *multisort.Sorter[*adminv2.TaskInfo] { + return multisort.New(multisort.FieldMap[*adminv2.TaskInfo]{ + "id": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.Id, b.Id, descending) + }, + "queue": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.Queue, b.Queue, descending) + }, + "type": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.Type, b.Type, descending) + }, + "state": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(adminv2.TaskState_name[int32(a.State)], adminv2.TaskState_name[int32(b.State)], descending) + }, + "retried": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.Retried, b.Retried, descending) + }, + "completed-at": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.CompletedAt.AsTime().UnixMilli(), b.CompletedAt.AsTime().UnixMilli(), descending) + }, + "last-failed-at": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.LastFailedAt.AsTime().UnixMilli(), b.LastFailedAt.AsTime().UnixMilli(), descending) + }, + "deadline-at": func(a, b *adminv2.TaskInfo, descending bool) multisort.CompareResult { + return multisort.Compare(a.Deadline.AsTime().UnixMilli(), b.Deadline.AsTime().UnixMilli(), descending) + }, + }, multisort.Keys{{ID: "id"}}) +} diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index e8aaae0..933e6f9 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -83,6 +83,13 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin case []*apiv2.ProjectMember: return t.ProjectMemberTable(d, wide) + case *adminv2.TaskInfo: + return t.TaskTable(pointer.WrapInSlice(d), wide) + case []*adminv2.TaskInfo: + return t.TaskTable(d, wide) + case *adminv2.TaskServiceQueuesResponse: + return t.TaskQueueTable(d, wide) + case *apiv2.Token: return t.TokenTable(pointer.WrapInSlice(d), wide) case []*apiv2.Token: diff --git a/cmd/tableprinters/task.go b/cmd/tableprinters/task.go new file mode 100644 index 0000000..17f4b05 --- /dev/null +++ b/cmd/tableprinters/task.go @@ -0,0 +1,73 @@ +package tableprinters + +import ( + "time" + + "github.com/google/uuid" + "github.com/metal-stack/api/go/enum" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" +) + +func (t *TablePrinter) TaskTable(data []*adminv2.TaskInfo, wide bool) ([]string, [][]string, error) { + var ( + rows [][]string + ) + + header := []string{"ID", "Queue", "When", "Type", "State"} + + if wide { + header = []string{"ID", "Queue", "When", "Type", "State", "Issued At", "Payload", "Result"} + } + + for _, task := range data { + var ( + id = task.Id + queue = task.Queue + typeString = task.Type + payload = string(task.Payload) + result = string(task.Result) + ) + + state, err := enum.GetStringValue(task.State) + if err != nil { + state = new("unknown") + } + + parsed, err := uuid.Parse(id) + if err != nil { + return nil, nil, err + } + + var ( + sec, nano = parsed.Time().UnixTime() + issuedAt = time.Unix(sec, nano) + when = humanizeDuration(time.Since(issuedAt)) + ) + + if wide { + rows = append(rows, []string{id, queue, when, typeString, *state, issuedAt.String(), payload, result}) + } else { + rows = append(rows, []string{id, queue, when, typeString, *state}) + } + } + + t.t.DisableAutoWrap(false) + + return header, rows, nil +} + +func (t *TablePrinter) TaskQueueTable(data *adminv2.TaskServiceQueuesResponse, _ bool) ([]string, [][]string, error) { + var ( + rows [][]string + ) + + header := []string{"Queue"} + + for _, queue := range data.Queues { + rows = append(rows, []string{queue}) + } + + t.t.DisableAutoWrap(false) + + return header, rows, nil +} diff --git a/tests/e2e/admin/task_test.go b/tests/e2e/admin/task_test.go new file mode 100644 index 0000000..9b26251 --- /dev/null +++ b/tests/e2e/admin/task_test.go @@ -0,0 +1,203 @@ +package admin_e2e + +import ( + "testing" + + "connectrpc.com/connect" + "github.com/metal-stack/api/go/client" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/metal-stack/cli/testing/e2e" + "github.com/metal-stack/cli/tests/e2e/testresources" +) + +func Test_AdminTaskCmd_List(t *testing.T) { + tests := []*e2e.Test[adminv2.TaskServiceListResponse, adminv2.TaskInfo]{ + { + Name: "list", + CmdArgs: []string{"admin", "task", "list"}, + NewRootCmd: e2e.NewRootCmd(t, &e2e.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.TaskServiceListRequest{}, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.TaskServiceListResponse{ + Tasks: []*adminv2.TaskInfo{ + testresources.Task1(), + testresources.Task2(), + }, + }) + }, + }, + }, + }), + Template: new("{{ .id }} {{ .type }}"), + WantTemplate: new(` +550e8400-e29b-41d4-a716-446655440000 image-provision +550e8400-e29b-41d4-a716-446655440001 firewall-update + `), + WantTable: new(` + ID QUEUE WHEN TYPE STATE + 550e8400-e29b-41d4-a716-446655440000 default -369d -23h image-provision active + 550e8400-e29b-41d4-a716-446655440001 default -369d -23h firewall-update pending + `), + WantWideTable: new(` + ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT + 550e8400-e29b-41d4-a716-446655440000 default -369d -23h image-provision active 2001-01-05 00:43:07.540992 +0100 CET {"machine_id":"machine1"} + 550e8400-e29b-41d4-a716-446655440001 default -369d -23h firewall-update pending 2001-01-05 00:43:07.540992 +0100 CET {"firewall_id":"fw1"} + `), + WantMarkdown: new(` + | ID | QUEUE | WHEN | TYPE | STATE | + |--------------------------------------|---------|------------|-----------------|---------| + | 550e8400-e29b-41d4-a716-446655440000 | default | -369d -23h | image-provision | active | + | 550e8400-e29b-41d4-a716-446655440001 | default | -369d -23h | firewall-update | pending | + `), + }, + { + Name: "list with queue filter", + CmdArgs: []string{"admin", "task", "list", "--queue", "high-priority"}, + NewRootCmd: e2e.NewRootCmd(t, &e2e.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.TaskServiceListRequest{ + Queue: func() *string { s := "high-priority"; return &s }(), + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.TaskServiceListResponse{ + Tasks: []*adminv2.TaskInfo{ + testresources.Task3(), + }, + }) + }, + }, + }, + }), + WantTable: new(` + ID QUEUE WHEN TYPE STATE + 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminTaskCmd_Describe(t *testing.T) { + tests := []*e2e.Test[adminv2.TaskServiceGetResponse, *adminv2.TaskInfo]{ + { + Name: "describe", + CmdArgs: []string{"admin", "task", "describe", testresources.Task1().Id}, + NewRootCmd: e2e.NewRootCmd(t, &e2e.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.TaskServiceGetRequest{ + TaskId: testresources.Task1().Id, + Queue: "default", + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.TaskServiceGetResponse{ + Task: testresources.Task1(), + }) + }, + }, + }, + }), + WantObject: testresources.Task1(), + WantProtoObject: testresources.Task1(), + Template: new("{{ .id }} {{ .type }}"), + WantTemplate: new(` +550e8400-e29b-41d4-a716-446655440000 image-provision + `), + }, + { + Name: "describe with queue", + CmdArgs: []string{"admin", "task", "describe", testresources.Task3().Id, "--queue", "high-priority"}, + NewRootCmd: e2e.NewRootCmd(t, &e2e.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.TaskServiceGetRequest{ + TaskId: testresources.Task3().Id, + Queue: "high-priority", + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.TaskServiceGetResponse{ + Task: testresources.Task3(), + }) + }, + }, + }, + }), + WantObject: testresources.Task3(), + WantProtoObject: testresources.Task3(), + Template: new("{{ .id }} {{ .queue }} {{ .type }}"), + WantTemplate: new(` +550e8400-e29b-41d4-a716-446655440002 high-priority machine-reimage + `), + WantTable: new(` + ID QUEUE WHEN TYPE STATE + 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed + `), + WantWideTable: new(` + ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT + 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed 2001-01-05 00:43:07.540992 +0100 CET {"machine_id":"machine2"} success + `), + WantMarkdown: new(` + | ID | QUEUE | WHEN | TYPE | STATE | + |--------------------------------------|---------------|------------|-----------------|-----------| + | 550e8400-e29b-41d4-a716-446655440002 | high-priority | -369d -23h | machine-reimage | completed | + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminTaskQueuesCmd(t *testing.T) { + tests := []*e2e.Test[adminv2.TaskServiceQueuesResponse, any]{ + { + Name: "queues", + CmdArgs: []string{"admin", "task", "queues"}, + NewRootCmd: e2e.NewRootCmd(t, &e2e.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.TaskServiceQueuesRequest{}, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.TaskServiceQueuesResponse{ + Queues: []string{"default", "high-priority", "low-priority"}, + }) + }, + }, + }, + }), + WantTable: new(` + QUEUE + default + high-priority + low-priority + `), + WantWideTable: new(` + QUEUE + default + high-priority + low-priority + `), + WantMarkdown: new(` + | QUEUE | + |---------------| + | default | + | high-priority | + | low-priority | + `), + Template: new(`{{ range .queues }}{{ . }}{{ "\n" }}{{ end }}`), + WantTemplate: new(` +default +high-priority +low-priority + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} diff --git a/tests/e2e/testresources/tasks.go b/tests/e2e/testresources/tasks.go new file mode 100644 index 0000000..83cac5e --- /dev/null +++ b/tests/e2e/testresources/tasks.go @@ -0,0 +1,68 @@ +package testresources + +import ( + "time" + + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/metal-stack/cli/testing/e2e" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var ( + Task1 = func() *adminv2.TaskInfo { + return &adminv2.TaskInfo{ + Id: "550e8400-e29b-41d4-a716-446655440000", + Queue: "default", + Type: "image-provision", + Payload: []byte(`{"machine_id":"machine1"}`), + State: adminv2.TaskState_TASK_STATE_ACTIVE, + MaxRetry: 3, + Retried: 1, + LastError: "connection timeout", + LastFailedAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(-5 * time.Minute)), + Timeout: durationpb.New(30 * time.Second), + Deadline: timestamppb.New(e2e.TimeBubbleStartTime().Add(5 * time.Minute)), + NextProcessAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(10 * time.Second)), + Retention: durationpb.New(24 * time.Hour), + CompletedAt: nil, + } + } + Task2 = func() *adminv2.TaskInfo { + return &adminv2.TaskInfo{ + Id: "550e8400-e29b-41d4-a716-446655440001", + Queue: "default", + Type: "firewall-update", + Payload: []byte(`{"firewall_id":"fw1"}`), + State: adminv2.TaskState_TASK_STATE_PENDING, + MaxRetry: 5, + Retried: 0, + LastError: "", + LastFailedAt: nil, + Timeout: durationpb.New(60 * time.Second), + Deadline: timestamppb.New(e2e.TimeBubbleStartTime().Add(10 * time.Minute)), + NextProcessAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(2 * time.Second)), + Retention: durationpb.New(48 * time.Hour), + CompletedAt: nil, + } + } + Task3 = func() *adminv2.TaskInfo { + return &adminv2.TaskInfo{ + Id: "550e8400-e29b-41d4-a716-446655440002", + Queue: "high-priority", + Type: "machine-reimage", + Payload: []byte(`{"machine_id":"machine2"}`), + State: adminv2.TaskState_TASK_STATE_COMPLETED, + MaxRetry: 3, + Retried: 0, + LastError: "", + LastFailedAt: nil, + Timeout: durationpb.New(5 * time.Minute), + Deadline: timestamppb.New(e2e.TimeBubbleStartTime().Add(30 * time.Minute)), + NextProcessAt: nil, + Retention: durationpb.New(7 * 24 * time.Hour), + CompletedAt: timestamppb.New(e2e.TimeBubbleStartTime().Add(-10 * time.Minute)), + Result: []byte(`success`), + } + } +) From df9ea5f462ba3862caf3427969b8e4ba3cdf1f89 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 20 May 2026 14:12:48 +0200 Subject: [PATCH 2/5] Cleanup. --- cmd/login.go | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index 02b1565..7e14376 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -128,7 +128,7 @@ func (l *login) login() error { } tokenResp, err := mc.Apiv2().Token().Create(context.Background(), &apiv2.TokenServiceCreateRequest{ - Description: "admin access issues by metal cli", + Description: "admin access issued by metal cli", Expires: durationpb.New(3 * time.Hour), AdminRole: new(apiv2.AdminRole((apiv2.AdminRole_value[viper.GetString("admin-role")]))), }) diff --git a/go.mod b/go.mod index 1b1a45a..d47939f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 + github.com/google/uuid v1.6.0 github.com/metal-stack/api v0.0.61 github.com/metal-stack/metal-lib v0.24.0 github.com/metal-stack/v v1.0.3 @@ -41,7 +42,6 @@ require ( github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/cel-go v0.28.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.5 // indirect github.com/klauspost/connect-compress/v2 v2.1.1 // indirect From 449d3bd7d097c0d0c9a6fe4db959bd2239f8a804 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 20 May 2026 14:33:31 +0200 Subject: [PATCH 3/5] Fixes. --- cmd/tableprinters/task.go | 5 +++-- pkg/helpers/audit/audit.go | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/tableprinters/task.go b/cmd/tableprinters/task.go index 17f4b05..c918fc1 100644 --- a/cmd/tableprinters/task.go +++ b/cmd/tableprinters/task.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/metal-stack/api/go/enum" adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/metal-stack/metal-lib/pkg/genericcli" ) func (t *TablePrinter) TaskTable(data []*adminv2.TaskInfo, wide bool) ([]string, [][]string, error) { @@ -24,8 +25,8 @@ func (t *TablePrinter) TaskTable(data []*adminv2.TaskInfo, wide bool) ([]string, id = task.Id queue = task.Queue typeString = task.Type - payload = string(task.Payload) - result = string(task.Result) + payload = genericcli.TruncateEnd(string(task.Payload), 40) + result = genericcli.TruncateEnd(string(task.Result), 40) ) state, err := enum.GetStringValue(task.State) diff --git a/pkg/helpers/audit/audit.go b/pkg/helpers/audit/audit.go index 442d334..7a3c1b0 100644 --- a/pkg/helpers/audit/audit.go +++ b/pkg/helpers/audit/audit.go @@ -34,7 +34,7 @@ func ToPhase(phase string) *apiv2.AuditPhase { return new(apiv2.AuditPhase(p)) } -func TryPrettifyBody(trace *apiv2.AuditTrace) *apiv2.AuditTrace { +func TryPrettifyBody(trace *apiv2.AuditTrace) { if trace.Body != nil { trimmed := strings.Trim(*trace.Body, `"`) body := map[string]any{} @@ -44,6 +44,4 @@ func TryPrettifyBody(trace *apiv2.AuditTrace) *apiv2.AuditTrace { } } } - - return trace } From 54e36db2c4865f28235c593f2655594a486104b3 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 20 May 2026 14:54:29 +0200 Subject: [PATCH 4/5] UUIDs for testing. --- cmd/tableprinters/task.go | 2 +- tests/e2e/admin/task_test.go | 46 ++++++++++++++++---------------- tests/e2e/testresources/tasks.go | 6 ++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/cmd/tableprinters/task.go b/cmd/tableprinters/task.go index c918fc1..6666ad1 100644 --- a/cmd/tableprinters/task.go +++ b/cmd/tableprinters/task.go @@ -42,7 +42,7 @@ func (t *TablePrinter) TaskTable(data []*adminv2.TaskInfo, wide bool) ([]string, var ( sec, nano = parsed.Time().UnixTime() issuedAt = time.Unix(sec, nano) - when = humanizeDuration(time.Since(issuedAt)) + when = humanizeDuration(time.Since(issuedAt)) + " ago" ) if wide { diff --git a/tests/e2e/admin/task_test.go b/tests/e2e/admin/task_test.go index 9b26251..1a9ddac 100644 --- a/tests/e2e/admin/task_test.go +++ b/tests/e2e/admin/task_test.go @@ -32,24 +32,24 @@ func Test_AdminTaskCmd_List(t *testing.T) { }), Template: new("{{ .id }} {{ .type }}"), WantTemplate: new(` -550e8400-e29b-41d4-a716-446655440000 image-provision -550e8400-e29b-41d4-a716-446655440001 firewall-update +00dc6a98-bd80-787a-9725-ffb692d83261 image-provision +00dc6ab4-34c0-73ce-af77-3e07748d0e0d firewall-update `), WantTable: new(` - ID QUEUE WHEN TYPE STATE - 550e8400-e29b-41d4-a716-446655440000 default -369d -23h image-provision active - 550e8400-e29b-41d4-a716-446655440001 default -369d -23h firewall-update pending + ID QUEUE WHEN TYPE STATE + 00dc6a98-bd80-787a-9725-ffb692d83261 default 1h ago image-provision active + 00dc6ab4-34c0-73ce-af77-3e07748d0e0d default 30m ago firewall-update pending `), WantWideTable: new(` - ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT - 550e8400-e29b-41d4-a716-446655440000 default -369d -23h image-provision active 2001-01-05 00:43:07.540992 +0100 CET {"machine_id":"machine1"} - 550e8400-e29b-41d4-a716-446655440001 default -369d -23h firewall-update pending 2001-01-05 00:43:07.540992 +0100 CET {"firewall_id":"fw1"} + ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT + 00dc6a98-bd80-787a-9725-ffb692d83261 default 1h ago image-provision active 2000-01-01 00:00:00 +0100 CET {"machine_id":"machine1"} + 00dc6ab4-34c0-73ce-af77-3e07748d0e0d default 30m ago firewall-update pending 2000-01-01 00:30:00 +0100 CET {"firewall_id":"fw1"} `), WantMarkdown: new(` - | ID | QUEUE | WHEN | TYPE | STATE | - |--------------------------------------|---------|------------|-----------------|---------| - | 550e8400-e29b-41d4-a716-446655440000 | default | -369d -23h | image-provision | active | - | 550e8400-e29b-41d4-a716-446655440001 | default | -369d -23h | firewall-update | pending | + | ID | QUEUE | WHEN | TYPE | STATE | + |--------------------------------------|---------|---------|-----------------|---------| + | 00dc6a98-bd80-787a-9725-ffb692d83261 | default | 1h ago | image-provision | active | + | 00dc6ab4-34c0-73ce-af77-3e07748d0e0d | default | 30m ago | firewall-update | pending | `), }, { @@ -72,8 +72,8 @@ func Test_AdminTaskCmd_List(t *testing.T) { }, }), WantTable: new(` - ID QUEUE WHEN TYPE STATE - 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed + ID QUEUE WHEN TYPE STATE + 00dc6ab5-1f20-7426-a397-8644fb78324e high-priority 29m ago machine-reimage completed `), }, } @@ -106,7 +106,7 @@ func Test_AdminTaskCmd_Describe(t *testing.T) { WantProtoObject: testresources.Task1(), Template: new("{{ .id }} {{ .type }}"), WantTemplate: new(` -550e8400-e29b-41d4-a716-446655440000 image-provision +00dc6a98-bd80-787a-9725-ffb692d83261 image-provision `), }, { @@ -131,20 +131,20 @@ func Test_AdminTaskCmd_Describe(t *testing.T) { WantProtoObject: testresources.Task3(), Template: new("{{ .id }} {{ .queue }} {{ .type }}"), WantTemplate: new(` -550e8400-e29b-41d4-a716-446655440002 high-priority machine-reimage +00dc6ab5-1f20-7426-a397-8644fb78324e high-priority machine-reimage `), WantTable: new(` - ID QUEUE WHEN TYPE STATE - 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed + ID QUEUE WHEN TYPE STATE + 00dc6ab5-1f20-7426-a397-8644fb78324e high-priority 29m ago machine-reimage completed `), WantWideTable: new(` - ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT - 550e8400-e29b-41d4-a716-446655440002 high-priority -369d -23h machine-reimage completed 2001-01-05 00:43:07.540992 +0100 CET {"machine_id":"machine2"} success + ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT + 00dc6ab5-1f20-7426-a397-8644fb78324e high-priority 29m ago machine-reimage completed 2000-01-01 00:31:00 +0100 CET {"machine_id":"machine2"} success `), WantMarkdown: new(` - | ID | QUEUE | WHEN | TYPE | STATE | - |--------------------------------------|---------------|------------|-----------------|-----------| - | 550e8400-e29b-41d4-a716-446655440002 | high-priority | -369d -23h | machine-reimage | completed | + | ID | QUEUE | WHEN | TYPE | STATE | + |--------------------------------------|---------------|---------|-----------------|-----------| + | 00dc6ab5-1f20-7426-a397-8644fb78324e | high-priority | 29m ago | machine-reimage | completed | `), }, } diff --git a/tests/e2e/testresources/tasks.go b/tests/e2e/testresources/tasks.go index 83cac5e..1c6cbc2 100644 --- a/tests/e2e/testresources/tasks.go +++ b/tests/e2e/testresources/tasks.go @@ -12,7 +12,7 @@ import ( var ( Task1 = func() *adminv2.TaskInfo { return &adminv2.TaskInfo{ - Id: "550e8400-e29b-41d4-a716-446655440000", + Id: "00dc6a98-bd80-787a-9725-ffb692d83261", Queue: "default", Type: "image-provision", Payload: []byte(`{"machine_id":"machine1"}`), @@ -30,7 +30,7 @@ var ( } Task2 = func() *adminv2.TaskInfo { return &adminv2.TaskInfo{ - Id: "550e8400-e29b-41d4-a716-446655440001", + Id: "00dc6ab4-34c0-73ce-af77-3e07748d0e0d", Queue: "default", Type: "firewall-update", Payload: []byte(`{"firewall_id":"fw1"}`), @@ -48,7 +48,7 @@ var ( } Task3 = func() *adminv2.TaskInfo { return &adminv2.TaskInfo{ - Id: "550e8400-e29b-41d4-a716-446655440002", + Id: "00dc6ab5-1f20-7426-a397-8644fb78324e", Queue: "high-priority", Type: "machine-reimage", Payload: []byte(`{"machine_id":"machine2"}`), From 543b6b34dd9ceae0a58c74ee4834c9fb4a5befba Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 20 May 2026 14:58:44 +0200 Subject: [PATCH 5/5] Fix output time. --- cmd/tableprinters/task.go | 2 +- tests/e2e/admin/task_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/tableprinters/task.go b/cmd/tableprinters/task.go index 6666ad1..34ea6d9 100644 --- a/cmd/tableprinters/task.go +++ b/cmd/tableprinters/task.go @@ -41,7 +41,7 @@ func (t *TablePrinter) TaskTable(data []*adminv2.TaskInfo, wide bool) ([]string, var ( sec, nano = parsed.Time().UnixTime() - issuedAt = time.Unix(sec, nano) + issuedAt = time.Unix(sec, nano).UTC() when = humanizeDuration(time.Since(issuedAt)) + " ago" ) diff --git a/tests/e2e/admin/task_test.go b/tests/e2e/admin/task_test.go index 1a9ddac..d8194fe 100644 --- a/tests/e2e/admin/task_test.go +++ b/tests/e2e/admin/task_test.go @@ -42,8 +42,8 @@ func Test_AdminTaskCmd_List(t *testing.T) { `), WantWideTable: new(` ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT - 00dc6a98-bd80-787a-9725-ffb692d83261 default 1h ago image-provision active 2000-01-01 00:00:00 +0100 CET {"machine_id":"machine1"} - 00dc6ab4-34c0-73ce-af77-3e07748d0e0d default 30m ago firewall-update pending 2000-01-01 00:30:00 +0100 CET {"firewall_id":"fw1"} + 00dc6a98-bd80-787a-9725-ffb692d83261 default 1h ago image-provision active 1999-12-31 23:00:00 +0000 UTC {"machine_id":"machine1"} + 00dc6ab4-34c0-73ce-af77-3e07748d0e0d default 30m ago firewall-update pending 1999-12-31 23:30:00 +0000 UTC {"firewall_id":"fw1"} `), WantMarkdown: new(` | ID | QUEUE | WHEN | TYPE | STATE | @@ -139,7 +139,7 @@ func Test_AdminTaskCmd_Describe(t *testing.T) { `), WantWideTable: new(` ID QUEUE WHEN TYPE STATE ISSUED AT PAYLOAD RESULT - 00dc6ab5-1f20-7426-a397-8644fb78324e high-priority 29m ago machine-reimage completed 2000-01-01 00:31:00 +0100 CET {"machine_id":"machine2"} success + 00dc6ab5-1f20-7426-a397-8644fb78324e high-priority 29m ago machine-reimage completed 1999-12-31 23:31:00 +0000 UTC {"machine_id":"machine2"} success `), WantMarkdown: new(` | ID | QUEUE | WHEN | TYPE | STATE |