Skip to content

Commit f73e2d6

Browse files
authored
Add json output format for kpt live status command (#2362)
1 parent 8c4acb5 commit f73e2d6

File tree

8 files changed

+104
-97
lines changed

8 files changed

+104
-97
lines changed

commands/livecmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func GetLiveCommand(ctx context.Context, _, version string) *cobra.Command {
5757
initCmd := cmdliveinit.NewCommand(ctx, f, ioStreams)
5858
applyCmd := cmdapply.NewCommand(ctx, rgProvider, ioStreams)
5959
destroyCmd := cmddestroy.NewCommand(ctx, rgProvider, ioStreams)
60-
statusCmd := status.NewCommand(ctx, rgProvider, ioStreams)
60+
statusCmd := status.NewCommand(ctx, rgProvider)
6161
installRGCmd := GetInstallRGRunner(f, ioStreams).Command
6262
liveCmd.AddCommand(initCmd, applyCmd, destroyCmd, statusCmd, installRGCmd)
6363

thirdparty/cli-utils/status/cmdstatus.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import (
1010
"time"
1111

1212
"github.com/GoogleContainerTools/kpt/internal/docs/generated/livedocs"
13+
"github.com/GoogleContainerTools/kpt/internal/printer"
1314
"github.com/GoogleContainerTools/kpt/internal/util/strings"
1415
"github.com/GoogleContainerTools/kpt/pkg/live"
15-
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers"
16+
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers"
17+
statusprinters "github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers"
1618
"github.com/go-errors/errors"
1719
"github.com/spf13/cobra"
1820
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -40,13 +42,11 @@ var (
4042
PollUntilOptions = []string{Known, Current, Deleted, Forever}
4143
)
4244

43-
func NewRunner(ctx context.Context, provider provider.Provider,
44-
ioStreams genericclioptions.IOStreams) *Runner {
45+
func NewRunner(ctx context.Context, provider provider.Provider) *Runner {
4546
r := &Runner{
4647
ctx: ctx,
4748
provider: provider,
4849
pollerFactoryFunc: pollerFactoryFunc,
49-
ioStreams: ioStreams,
5050
}
5151
c := &cobra.Command{
5252
Use: "status [PKG_PATH | -]",
@@ -67,18 +67,16 @@ func NewRunner(ctx context.Context, provider provider.Provider,
6767
return r
6868
}
6969

70-
func NewCommand(ctx context.Context, provider provider.Provider,
71-
ioStreams genericclioptions.IOStreams) *cobra.Command {
72-
return NewRunner(ctx, provider, ioStreams).Command
70+
func NewCommand(ctx context.Context, provider provider.Provider) *cobra.Command {
71+
return NewRunner(ctx, provider).Command
7372
}
7473

7574
// Runner captures the parameters for the command and contains
7675
// the run function.
7776
type Runner struct {
78-
ctx context.Context
79-
Command *cobra.Command
80-
ioStreams genericclioptions.IOStreams
81-
provider provider.Provider
77+
ctx context.Context
78+
Command *cobra.Command
79+
provider provider.Provider
8280

8381
period time.Duration
8482
pollUntil string
@@ -93,13 +91,18 @@ func (r *Runner) preRunE(*cobra.Command, []string) error {
9391
return fmt.Errorf("pollUntil must be one of %s",
9492
strings.JoinStringsWithQuotes(PollUntilOptions))
9593
}
94+
95+
if found := printers.ValidatePrinterType(r.output); !found {
96+
return fmt.Errorf("unknown output type %q", r.output)
97+
}
9698
return nil
9799
}
98100

99101
// runE implements the logic of the command and will delegate to the
100102
// poller to compute status for each of the resources. One of the printer
101103
// implementations takes care of printing the output.
102104
func (r *Runner) runE(c *cobra.Command, args []string) error {
105+
pr := printer.FromContextOrDie(r.ctx)
103106
if len(args) == 0 {
104107
// default to the current working directory
105108
cwd, err := os.Getwd()
@@ -138,7 +141,7 @@ func (r *Runner) runE(c *cobra.Command, args []string) error {
138141

139142
// Exit here if the inventory is empty.
140143
if len(identifiers) == 0 {
141-
_, _ = fmt.Fprint(r.ioStreams.Out, "no resources found in the inventory\n")
144+
pr.Printf("no resources found in the inventory\n")
142145
return nil
143146
}
144147

@@ -149,7 +152,10 @@ func (r *Runner) runE(c *cobra.Command, args []string) error {
149152

150153
// Fetch a printer implementation based on the desired output format as
151154
// specified in the output flag.
152-
printer, err := printers.CreatePrinter(r.output, r.ioStreams)
155+
printer, err := statusprinters.CreatePrinter(r.output, genericclioptions.IOStreams{
156+
Out: pr.OutStream(),
157+
ErrOut: pr.ErrStream(),
158+
})
153159
if err != nil {
154160
return errors.WrapPrefix(err, "error creating printer", 1)
155161
}
@@ -186,8 +192,7 @@ func (r *Runner) runE(c *cobra.Command, args []string) error {
186192
UseCache: true,
187193
})
188194

189-
printer.Print(eventChannel, identifiers, cancelFunc)
190-
return nil
195+
return printer.Print(eventChannel, identifiers, cancelFunc)
191196
}
192197

193198
// desiredStatusNotifierFunc returns an Observer function for the

thirdparty/cli-utils/status/cmdstatus_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package status
55

66
import (
7+
"bytes"
78
"context"
89
"path/filepath"
910
"strings"
@@ -17,7 +18,6 @@ import (
1718
"github.com/GoogleContainerTools/kpt/pkg/live"
1819
"github.com/stretchr/testify/assert"
1920
"k8s.io/apimachinery/pkg/runtime/schema"
20-
"k8s.io/cli-runtime/pkg/genericclioptions"
2121
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
2222
cmdutil "k8s.io/kubectl/pkg/cmd/util"
2323
"sigs.k8s.io/cli-utils/pkg/apply/poller"
@@ -239,7 +239,6 @@ deployment.apps/foo is InProgress: inProgress
239239
t.Run(tn, func(t *testing.T) {
240240
tf := cmdtesting.NewTestFactory().WithNamespace("namespace")
241241
defer tf.Cleanup()
242-
ioStreams, _, outBuf, _ := genericclioptions.NewTestIOStreams() //nolint:dogsled
243242

244243
w, clean := testutil.SetupWorkspace(t)
245244
defer clean()
@@ -250,8 +249,9 @@ deployment.apps/foo is InProgress: inProgress
250249
revert := testutil.Chdir(t, w.WorkspaceDirectory)
251250
defer revert()
252251

252+
var outBuf bytes.Buffer
253253
provider := live.NewFakeResourceGroupProvider(tf, tc.inventory)
254-
runner := NewRunner(fake.CtxWithDefaultPrinter(), provider, ioStreams)
254+
runner := NewRunner(fake.CtxWithPrinter(&outBuf, &outBuf), provider)
255255
runner.pollerFactoryFunc = func(c cmdutil.Factory) (poller.Poller, error) {
256256
return &fakePoller{tc.events}, nil
257257
}

thirdparty/cli-utils/status/printers/event/event_printer.go

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package list
2+
3+
import (
4+
"sigs.k8s.io/cli-utils/pkg/apply/event"
5+
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/collector"
6+
pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
7+
"sigs.k8s.io/cli-utils/pkg/object"
8+
"sigs.k8s.io/cli-utils/pkg/print/list"
9+
)
10+
11+
// BaseListPrinter implements the Printer interface and outputs the resource
12+
// status information as a list of events as they happen.
13+
type BaseListPrinter struct {
14+
Formatter list.Formatter
15+
}
16+
17+
// Print takes an event channel and outputs the status events on the channel
18+
// until the channel is closed. The provided cancelFunc is consulted on
19+
// every event and is responsible for stopping the poller when appropriate.
20+
// This function will block.
21+
func (ep *BaseListPrinter) Print(ch <-chan pollevent.Event, identifiers []object.ObjMetadata,
22+
cancelFunc collector.ObserverFunc) error {
23+
coll := collector.NewResourceStatusCollector(identifiers)
24+
// The actual work is done by the collector, which will invoke the
25+
// callback on every event. In the callback we print the status
26+
// information and call the cancelFunc which is responsible for
27+
// stopping the poller at the correct time.
28+
done := coll.ListenWithObserver(ch, collector.ObserverFunc(
29+
func(statusCollector *collector.ResourceStatusCollector, e pollevent.Event) {
30+
err := ep.printStatusEvent(e)
31+
if err != nil {
32+
panic(err)
33+
}
34+
cancelFunc(statusCollector, e)
35+
}),
36+
)
37+
// Block until the done channel is closed.
38+
<-done
39+
if o := coll.LatestObservation(); o.Error != nil {
40+
return o.Error
41+
}
42+
return nil
43+
}
44+
45+
func (ep *BaseListPrinter) printStatusEvent(se pollevent.Event) error {
46+
switch se.EventType {
47+
case pollevent.ResourceUpdateEvent:
48+
id := se.Resource.Identifier
49+
return ep.Formatter.FormatStatusEvent(event.StatusEvent{
50+
Identifier: id,
51+
Resource: se.Resource.Resource,
52+
PollResourceInfo: se.Resource,
53+
})
54+
case pollevent.ErrorEvent:
55+
return ep.Formatter.FormatErrorEvent(event.ErrorEvent{
56+
Err: se.Error,
57+
})
58+
}
59+
return nil
60+
}

thirdparty/cli-utils/status/printers/printer/printer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ type Printer interface {
2121
// needs to, and that it has completed shutting down. The latter is important
2222
// to make sure the printer has a chance to output all data before the
2323
// program terminates.
24-
Print(ch <-chan event.Event, identifiers []object.ObjMetadata, cancelFunc collector.ObserverFunc)
24+
Print(ch <-chan event.Event, identifiers []object.ObjMetadata, cancelFunc collector.ObserverFunc) error
2525
}

thirdparty/cli-utils/status/printers/printers.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@
44
package printers
55

66
import (
7-
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers/event"
7+
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers"
8+
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/events"
9+
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/printers/json"
10+
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers/list"
811
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers/printer"
912
"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/status/printers/table"
10-
1113
"k8s.io/cli-runtime/pkg/genericclioptions"
14+
"sigs.k8s.io/cli-utils/pkg/common"
1215
)
1316

1417
// CreatePrinter return an implementation of the Printer interface. The
1518
// actual implementation is based on the printerType requested.
1619
func CreatePrinter(printerType string, ioStreams genericclioptions.IOStreams) (printer.Printer, error) {
1720
switch printerType {
18-
case "table":
21+
case printers.TablePrinter:
1922
return table.NewTablePrinter(ioStreams), nil
23+
case printers.JSONPrinter:
24+
return &list.BaseListPrinter{
25+
Formatter: json.NewFormatter(ioStreams, common.DryRunNone),
26+
}, nil
2027
default:
21-
return event.NewEventPrinter(ioStreams), nil
28+
return &list.BaseListPrinter{
29+
Formatter: events.NewFormatter(ioStreams, common.DryRunNone),
30+
}, nil
2231
}
2332
}

thirdparty/cli-utils/status/printers/table/table_printer.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func NewTablePrinter(ioStreams genericclioptions.IOStreams) *tablePrinter {
3535
// until the channel is closed .
3636
//nolint:interfacer
3737
func (t *tablePrinter) Print(ch <-chan event.Event, identifiers []object.ObjMetadata,
38-
cancelFunc collector.ObserverFunc) {
38+
cancelFunc collector.ObserverFunc) error {
3939
coll := collector.NewResourceStatusCollector(identifiers)
4040
stop := make(chan struct{})
4141

@@ -51,6 +51,10 @@ func (t *tablePrinter) Print(ch <-chan event.Event, identifiers []object.ObjMeta
5151
// Block until all the collector has shut down. This means the
5252
// eventChannel has been closed and all events have been processed.
5353
<-done
54+
var err error
55+
if o := coll.LatestObservation(); o.Error != nil {
56+
err = o.Error
57+
}
5458

5559
// Close the stop channel to notify the print goroutine that it should
5660
// shut down.
@@ -60,6 +64,7 @@ func (t *tablePrinter) Print(ch <-chan event.Event, identifiers []object.ObjMeta
6064
// the printer has updated the UI with the latest state and
6165
// exited from the goroutine.
6266
<-printCompleted
67+
return err
6368
}
6469

6570
var columns = []table.ColumnDefinition{

0 commit comments

Comments
 (0)