From cc92935fcb96aa9e4e624d66924acdb90b11d3e8 Mon Sep 17 00:00:00 2001 From: Johnny Steenbergen Date: Thu, 26 Mar 2020 13:23:14 -0700 Subject: [PATCH 1/2] feat(pkger): add stack init cmd to influx cli closes: #17235 --- CHANGELOG.md | 1 + cmd/influx/main.go | 5 ++ cmd/influx/pkg.go | 67 +++++++++++++++ cmd/influx/pkg_test.go | 134 ++++++++++++++++++++++++++++- cmd/influxd/launcher/pkger_test.go | 21 ++--- pkger/http_remote_service.go | 24 ++---- pkger/http_server.go | 26 ++---- pkger/service.go | 34 +++++--- pkger/service_logging.go | 23 +++-- pkger/store.go | 23 ++--- pkger/store_test.go | 24 ++---- 11 files changed, 269 insertions(+), 113 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62929db3eae..5681c708e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ 1. [17363](https://github.com/influxdata/influxdb/pull/17363): Telegraf config tokens can no longer be retrieved after creation, but new tokens can be created after a telegraf has been setup 1. [17400](https://github.com/influxdata/influxdb/pull/17400): Be able to delete bucket by name via cli 1. [17396](https://github.com/influxdata/influxdb/pull/17396): Add module to write line data to specified url, org, and bucket +1. [17448](https://github.com/influxdata/influxdb/pull/17448): Add foundation for pkger stacks, stateful package management ### Bug Fixes diff --git a/cmd/influx/main.go b/cmd/influx/main.go index 47442e065ac..9618041a569 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "fmt" "io" "io/ioutil" @@ -88,6 +89,10 @@ func (o genericCLIOpts) newCmd(use string, runE func(*cobra.Command, []string) e return cmd } +func (o genericCLIOpts) writeJSON(v interface{}) error { + return json.NewEncoder(o.w).Encode(v) +} + func (o genericCLIOpts) newTabWriter() *internal.TabWriter { return internal.NewTabWriter(o.w) } diff --git a/cmd/influx/pkg.go b/cmd/influx/pkg.go index 1655188ed28..1f1f45dbbd1 100644 --- a/cmd/influx/pkg.go +++ b/cmd/influx/pkg.go @@ -42,8 +42,11 @@ type cmdPkgBuilder struct { file string files []string filters []string + description string disableColor bool disableTableBorders bool + json bool + name string org organization quiet bool recurse bool @@ -82,7 +85,9 @@ func (b *cmdPkgBuilder) cmd() *cobra.Command { b.cmdPkgExport(), b.cmdPkgSummary(), b.cmdPkgValidate(), + b.cmdStack(), ) + return cmd } @@ -345,6 +350,68 @@ func (b *cmdPkgBuilder) cmdPkgValidate() *cobra.Command { return cmd } +func (b *cmdPkgBuilder) cmdStack() *cobra.Command { + cmd := b.newCmd("stack", nil, false) + cmd.Short = "Stack management commands" + cmd.AddCommand(b.cmdStackInit()) + return cmd +} + +func (b *cmdPkgBuilder) cmdStackInit() *cobra.Command { + cmd := b.newCmd("init", b.stackInitRunEFn, true) + cmd.Short = "Initialize a stack" + + cmd.Flags().StringVarP(&b.name, "stack-name", "n", "", "Name given to created stack") + cmd.Flags().StringVarP(&b.description, "stack-description", "d", "", "Description given to created stack") + cmd.Flags().StringArrayVarP(&b.urls, "package-url", "u", nil, "Package urls to associate with new stack") + cmd.Flags().BoolVar(&b.json, "json", false, "Output data as json") + + b.org.register(cmd, false) + + return cmd +} + +func (b *cmdPkgBuilder) stackInitRunEFn(cmd *cobra.Command, args []string) error { + pkgSVC, orgSVC, err := b.svcFn() + if err != nil { + return err + } + + orgID, err := b.org.getID(orgSVC) + if err != nil { + return err + } + + const fakeUserID = 0 // is 0 because user is pulled from token... + stack, err := pkgSVC.InitStack(context.Background(), fakeUserID, pkger.Stack{ + OrgID: orgID, + Name: b.name, + Description: b.description, + URLs: b.urls, + }) + if err != nil { + return err + } + + if b.json { + return b.writeJSON(stack) + } + + tabW := b.newTabWriter() + tabW.WriteHeaders("ID", "OrgID", "Name", "Description", "URLs", "Created At") + tabW.Write(map[string]interface{}{ + "ID": stack.ID, + "OrgID": stack.OrgID, + "Name": stack.Name, + "Description": stack.Description, + "URLs": stack.URLs, + "Created At": stack.CreatedAt, + }) + tabW.Flush() + + return nil +} + func (b *cmdPkgBuilder) registerPkgFileFlags(cmd *cobra.Command) { cmd.Flags().StringSliceVarP(&b.files, "file", "f", nil, "Path to package file") cmd.MarkFlagFilename("file", "yaml", "yml", "json", "jsonnet") diff --git a/cmd/influx/pkg_test.go b/cmd/influx/pkg_test.go index 96d53d55a4f..4daa0aad6ee 100644 --- a/cmd/influx/pkg_test.go +++ b/cmd/influx/pkg_test.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "encoding/json" "errors" "io" "io/ioutil" @@ -447,6 +448,122 @@ func TestCmdPkg(t *testing.T) { require.Error(t, cmd.Execute()) }) }) + + t.Run("stack", func(t *testing.T) { + t.Run("init", func(t *testing.T) { + tests := []struct { + name string + args []string + envVars map[string]string + expectedStack pkger.Stack + shouldErr bool + }{ + { + name: "when only org and token provided is successful", + args: []string{"--org-id=" + influxdb.ID(1).String()}, + expectedStack: pkger.Stack{ + OrgID: 1, + }, + }, + { + name: "when org and name provided provided is successful", + args: []string{ + "--org-id=" + influxdb.ID(1).String(), + "--stack-name=foo", + }, + expectedStack: pkger.Stack{ + OrgID: 1, + Name: "foo", + }, + }, + { + name: "when all flags provided provided is successful", + args: []string{ + "--org-id=" + influxdb.ID(1).String(), + "--stack-name=foo", + "--stack-description=desc", + "--package-url=http://example.com/1", + "--package-url=http://example.com/2", + }, + expectedStack: pkger.Stack{ + OrgID: 1, + Name: "foo", + Description: "desc", + URLs: []string{ + "http://example.com/1", + "http://example.com/2", + }, + }, + }, + { + name: "when all shorthand flags provided provided is successful", + args: []string{ + "--org-id=" + influxdb.ID(1).String(), + "-n=foo", + "-d=desc", + "-u=http://example.com/1", + "-u=http://example.com/2", + }, + expectedStack: pkger.Stack{ + OrgID: 1, + Name: "foo", + Description: "desc", + URLs: []string{ + "http://example.com/1", + "http://example.com/2", + }, + }, + }, + } + + for _, tt := range tests { + fn := func(t *testing.T) { + defer addEnvVars(t, envVarsZeroMap)() + + outBuf := new(bytes.Buffer) + defer func() { + if t.Failed() && outBuf.Len() > 0 { + t.Log(outBuf.String()) + } + }() + + builder := newInfluxCmdBuilder( + in(new(bytes.Buffer)), + out(outBuf), + ) + + rootCmd := builder.cmd(func(f *globalFlags, opt genericCLIOpts) *cobra.Command { + echoSVC := &fakePkgSVC{ + initStackFn: func(ctx context.Context, userID influxdb.ID, stack pkger.Stack) (pkger.Stack, error) { + stack.ID = 9000 + return stack, nil + }, + } + return newCmdPkgBuilder(fakeSVCFn(echoSVC), opt).cmd() + }) + + baseArgs := []string{"pkg", "stack", "init", "--json"} + + rootCmd.SetArgs(append(baseArgs, tt.args...)) + + err := rootCmd.Execute() + if tt.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + var stack pkger.Stack + testDecodeJSONBody(t, outBuf, &stack) + if tt.expectedStack.ID == 0 { + tt.expectedStack.ID = 9000 + } + assert.Equal(t, tt.expectedStack, stack) + } + } + + t.Run(tt.name, fn) + } + }) + }) } func Test_readFilesFromPath(t *testing.T) { @@ -586,12 +703,16 @@ func testPkgWritesToBuffer(newCmdFn func(w io.Writer) *cobra.Command, args pkgFi } type fakePkgSVC struct { - createFn func(ctx context.Context, setters ...pkger.CreatePkgSetFn) (*pkger.Pkg, error) - dryRunFn func(ctx context.Context, orgID, userID influxdb.ID, pkg *pkger.Pkg) (pkger.Summary, pkger.Diff, error) - applyFn func(ctx context.Context, orgID, userID influxdb.ID, pkg *pkger.Pkg, opts ...pkger.ApplyOptFn) (pkger.Summary, error) + initStackFn func(ctx context.Context, userID influxdb.ID, stack pkger.Stack) (pkger.Stack, error) + createFn func(ctx context.Context, setters ...pkger.CreatePkgSetFn) (*pkger.Pkg, error) + dryRunFn func(ctx context.Context, orgID, userID influxdb.ID, pkg *pkger.Pkg) (pkger.Summary, pkger.Diff, error) + applyFn func(ctx context.Context, orgID, userID influxdb.ID, pkg *pkger.Pkg, opts ...pkger.ApplyOptFn) (pkger.Summary, error) } func (f *fakePkgSVC) InitStack(ctx context.Context, userID influxdb.ID, stack pkger.Stack) (pkger.Stack, error) { + if f.initStackFn != nil { + return f.initStackFn(ctx, userID, stack) + } panic("not implemented") } @@ -639,3 +760,10 @@ func idsStr(ids ...influxdb.ID) string { } return strings.Join(idStrs, ",") } + +func testDecodeJSONBody(t *testing.T, r io.Reader, v interface{}) { + t.Helper() + + err := json.NewDecoder(r).Decode(v) + require.NoError(t, err) +} diff --git a/cmd/influxd/launcher/pkger_test.go b/cmd/influxd/launcher/pkger_test.go index 57628ce09de..481963b7880 100644 --- a/cmd/influxd/launcher/pkger_test.go +++ b/cmd/influxd/launcher/pkger_test.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net/url" "testing" "time" @@ -25,22 +24,22 @@ func TestLauncher_Pkger(t *testing.T) { svc := l.PkgerService(t) t.Run("creating a stack", func(t *testing.T) { - expectedURLs := []url.URL{newURL(t, "http://example.com")} + expectedURLs := []string{"http://example.com"} fmt.Println("org init id: ", l.Org.ID) newStack, err := svc.InitStack(timedCtx(5*time.Second), l.User.ID, pkger.Stack{ - OrgID: l.Org.ID, - Name: "first stack", - Desc: "desc", - URLs: expectedURLs, + OrgID: l.Org.ID, + Name: "first stack", + Description: "desc", + URLs: expectedURLs, }) require.NoError(t, err) assert.NotZero(t, newStack.ID) assert.Equal(t, l.Org.ID, newStack.OrgID) assert.Equal(t, "first stack", newStack.Name) - assert.Equal(t, "desc", newStack.Desc) + assert.Equal(t, "desc", newStack.Description) assert.Equal(t, expectedURLs, newStack.URLs) assert.NotNil(t, newStack.Resources) assert.NotZero(t, newStack.CRUDLog) @@ -1290,11 +1289,3 @@ func (f *fakeLabelSVC) CreateLabelMapping(ctx context.Context, m *influxdb.Label } return f.LabelService.CreateLabelMapping(ctx, m) } - -func newURL(t *testing.T, rawurl string) url.URL { - t.Helper() - - u, err := url.Parse(rawurl) - require.NoError(t, err) - return *u -} diff --git a/pkger/http_remote_service.go b/pkger/http_remote_service.go index da07bb33d4f..7d1ecd2e7fa 100644 --- a/pkger/http_remote_service.go +++ b/pkger/http_remote_service.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net/http" - "net/url" "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/pkg/httpc" @@ -21,10 +20,8 @@ func (s *HTTPRemoteService) InitStack(ctx context.Context, userID influxdb.ID, s reqBody := ReqCreateStack{ OrgID: stack.OrgID.String(), Name: stack.Name, - Description: stack.Desc, - } - for _, u := range stack.URLs { - reqBody.URLs = append(reqBody.URLs, u.String()) + Description: stack.Description, + URLs: stack.URLs, } var respBody RespCreateStack @@ -37,10 +34,11 @@ func (s *HTTPRemoteService) InitStack(ctx context.Context, userID influxdb.ID, s } newStack := Stack{ - Name: respBody.Name, - Desc: respBody.Description, - Resources: make([]StackResource, 0), - CRUDLog: respBody.CRUDLog, + Name: respBody.Name, + Description: respBody.Description, + URLs: respBody.URLs, + Resources: make([]StackResource, 0), + CRUDLog: respBody.CRUDLog, } id, err := influxdb.IDFromString(respBody.ID) @@ -57,14 +55,6 @@ func (s *HTTPRemoteService) InitStack(ctx context.Context, userID influxdb.ID, s } newStack.OrgID = *orgID - for _, rawurl := range respBody.URLs { - u, err := url.Parse(rawurl) - if err != nil { - return Stack{}, err - } - newStack.URLs = append(newStack.URLs, *u) - } - return newStack, nil } diff --git a/pkger/http_server.go b/pkger/http_server.go index 4e1fb0f0224..41e07f59c90 100644 --- a/pkger/http_server.go +++ b/pkger/http_server.go @@ -96,15 +96,6 @@ func (r *ReqCreateStack) orgID() influxdb.ID { return *orgID } -func (r *ReqCreateStack) urls() []url.URL { - urls := make([]url.URL, 0, len(r.URLs)) - for _, urlStr := range r.URLs { - u, _ := url.Parse(urlStr) - urls = append(urls, *u) - } - return urls -} - // RespCreateStack is the response body for the create stack call. type RespCreateStack struct { ID string `json:"id"` @@ -130,27 +121,22 @@ func (s *HTTPServer) createStack(w http.ResponseWriter, r *http.Request) { } stack, err := s.svc.InitStack(r.Context(), auth.GetUserID(), Stack{ - OrgID: reqBody.orgID(), - Name: reqBody.Name, - Desc: reqBody.Description, - URLs: reqBody.urls(), + OrgID: reqBody.orgID(), + Name: reqBody.Name, + Description: reqBody.Description, + URLs: reqBody.URLs, }) if err != nil { s.api.Err(w, err) return } - urlStrs := make([]string, 0, len(stack.URLs)) - for _, u := range stack.URLs { - urlStrs = append(urlStrs, u.String()) - } - s.api.Respond(w, http.StatusCreated, RespCreateStack{ ID: stack.ID.String(), OrgID: stack.OrgID.String(), Name: stack.Name, - Description: stack.Desc, - URLs: urlStrs, + Description: stack.Description, + URLs: stack.URLs, CRUDLog: stack.CRUDLog, }) } diff --git a/pkger/service.go b/pkger/service.go index 4584c9ee0c7..ad455b55729 100644 --- a/pkger/service.go +++ b/pkger/service.go @@ -25,12 +25,12 @@ type ( // platform. This stack is updated only after side effects of applying a pkg. // If the pkg is applied, and no changes are had, then the stack is not updated. Stack struct { - ID influxdb.ID - OrgID influxdb.ID - Name string - Desc string - URLs []url.URL - Resources []StackResource + ID influxdb.ID `json:"id"` + OrgID influxdb.ID `json:"orgID"` + Name string `json:"name"` + Description string `json:"description"` + URLs []string `json:"urls"` + Resources []StackResource `json:"resources"` influxdb.CRUDLog } @@ -38,10 +38,10 @@ type ( // StackResource is a record for an individual resource side effect genereated from // applying a pkg. StackResource struct { - APIVersion string - ID influxdb.ID - Kind Kind - Name string + APIVersion string `json:"apiVersion"` + ID influxdb.ID `json:"resourceID"` + Kind Kind `json:"kind"` + Name string `json:"pkgName"` } ) @@ -260,6 +260,10 @@ func NewService(opts ...ServiceSetterFn) *Service { // with urls that point to the location of packages that are included as part of the stack when // it is applied. func (s *Service) InitStack(ctx context.Context, userID influxdb.ID, stack Stack) (Stack, error) { + if err := validURLs(stack.URLs); err != nil { + return Stack{}, err + } + if _, err := s.orgSVC.FindOrganizationByID(ctx, stack.OrgID); err != nil { if influxdb.ErrorCode(err) == influxdb.ENotFound { msg := fmt.Sprintf("organization dependency does not exist for id[%q]", stack.OrgID.String()) @@ -2253,6 +2257,16 @@ func (a applyErrs) toError(resType, msg string) error { return errors.New(errMsg) } +func validURLs(urls []string) error { + for _, u := range urls { + if _, err := url.Parse(u); err != nil { + msg := fmt.Sprintf("url invalid for entry %q", u) + return toInfluxError(influxdb.EInvalid, msg) + } + } + return nil +} + func labelSlcToMap(labels []*label) map[string]*label { m := make(map[string]*label) for i := range labels { diff --git a/pkger/service_logging.go b/pkger/service_logging.go index 9e8dca1e06b..db797870e73 100644 --- a/pkger/service_logging.go +++ b/pkger/service_logging.go @@ -27,21 +27,18 @@ var _ SVC = (*loggingMW)(nil) func (s *loggingMW) InitStack(ctx context.Context, userID influxdb.ID, newStack Stack) (stack Stack, err error) { defer func(start time.Time) { - if err != nil { - urlStrs := make([]string, 0, len(newStack.URLs)) - for _, u := range newStack.URLs { - urlStrs = append(urlStrs, u.String()) - } - s.logger.Error( - "failed to init stack", - zap.Error(err), - zap.Duration("took", time.Since(start)), - zap.Stringer("orgID", newStack.OrgID), - zap.Stringer("userID", userID), - zap.Strings("urls", urlStrs), - ) + if err == nil { return } + + s.logger.Error( + "failed to init stack", + zap.Error(err), + zap.Duration("took", time.Since(start)), + zap.Stringer("orgID", newStack.OrgID), + zap.Stringer("userID", userID), + zap.Strings("urls", newStack.URLs), + ) }(time.Now()) return s.next.InitStack(ctx, userID, newStack) } diff --git a/pkger/store.go b/pkger/store.go index 6bcff00ee43..e443f6145ac 100644 --- a/pkger/store.go +++ b/pkger/store.go @@ -3,7 +3,6 @@ package pkger import ( "context" "encoding/json" - "net/url" "time" "github.com/influxdata/influxdb" @@ -175,19 +174,14 @@ func convertStackToEnt(stack Stack) (kv.Entity, error) { return kv.Entity{}, err } - urlStrs := make([]string, 0, len(stack.URLs)) - for _, u := range stack.URLs { - urlStrs = append(urlStrs, u.String()) - } - stEnt := entStack{ ID: idBytes, OrgID: orgIDBytes, Name: stack.Name, - Description: stack.Desc, + Description: stack.Description, CreatedAt: stack.CreatedAt, UpdatedAt: stack.UpdatedAt, - URLs: urlStrs, + URLs: stack.URLs, } for _, res := range stack.Resources { @@ -208,8 +202,9 @@ func convertStackToEnt(stack Stack) (kv.Entity, error) { func convertStackEntToStack(ent *entStack) (Stack, error) { stack := Stack{ - Name: ent.Name, - Desc: ent.Description, + Name: ent.Name, + Description: ent.Description, + URLs: ent.URLs, CRUDLog: influxdb.CRUDLog{ CreatedAt: ent.CreatedAt, UpdatedAt: ent.UpdatedAt, @@ -223,14 +218,6 @@ func convertStackEntToStack(ent *entStack) (Stack, error) { return Stack{}, err } - for _, urlStr := range ent.URLs { - u, err := url.Parse(urlStr) - if err != nil { - return Stack{}, err - } - stack.URLs = append(stack.URLs, *u) - } - for _, res := range ent.Resources { stackRes := StackResource{ APIVersion: res.APIVersion, diff --git a/pkger/store_test.go b/pkger/store_test.go index ea642d7a79d..ff68986eeac 100644 --- a/pkger/store_test.go +++ b/pkger/store_test.go @@ -2,7 +2,6 @@ package pkger_test import ( "context" - "net/url" "testing" "time" @@ -19,17 +18,17 @@ func TestStoreKV(t *testing.T) { stackStub := func(id, orgID influxdb.ID) pkger.Stack { now := time.Time{}.Add(10 * 365 * 24 * time.Hour) return pkger.Stack{ - ID: id, - OrgID: orgID, - Name: "threeve", - Desc: "desc", + ID: id, + OrgID: orgID, + Name: "threeve", + Description: "desc", CRUDLog: influxdb.CRUDLog{ CreatedAt: now, UpdatedAt: now.Add(time.Hour), }, - URLs: []url.URL{ - newURL(t, "http://example.com"), - newURL(t, "http://abc.gov"), + URLs: []string{ + "http://example.com", + "http://abc.gov", }, Resources: []pkger.StackResource{ { @@ -197,12 +196,3 @@ func seedEntities(t *testing.T, store pkger.Store, first pkger.Stack, rest ...pk require.NoError(t, err) } } - -func newURL(t *testing.T, rawurl string) url.URL { - t.Helper() - - u, err := url.Parse(rawurl) - require.NoError(t, err) - - return *u -} From fb5e2a46d18d6620dd2c400439270ab56716b3b0 Mon Sep 17 00:00:00 2001 From: Johnny Steenbergen Date: Thu, 26 Mar 2020 13:35:03 -0700 Subject: [PATCH 2/2] feat(influx): add json output flag for pkg commands that print tables --- cmd/influx/main.go | 31 +++++++++++++++++++++++++++++- cmd/influx/pkg.go | 47 +++++++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/cmd/influx/main.go b/cmd/influx/main.go index 9618041a569..63ec6c2acee 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -90,7 +90,7 @@ func (o genericCLIOpts) newCmd(use string, runE func(*cobra.Command, []string) e } func (o genericCLIOpts) writeJSON(v interface{}) error { - return json.NewEncoder(o.w).Encode(v) + return writeJSON(o.w, v) } func (o genericCLIOpts) newTabWriter() *internal.TabWriter { @@ -441,12 +441,41 @@ func (f flagOpts) mustRegister(cmd *cobra.Command) { cli.BindOptions(cmd, f) } +func registerPrintOptions(cmd *cobra.Command, headersP, jsonOutP *bool) { + var opts flagOpts + if headersP != nil { + opts = append(opts, cli.Opt{ + DestP: headersP, + Flag: "hide-headers", + EnvVar: "HIDE_HEADERS", + Desc: "Hide the table headers; defaults false", + Default: false, + }) + } + if jsonOutP != nil { + opts = append(opts, cli.Opt{ + DestP: jsonOutP, + Flag: "json", + EnvVar: "OUTPUT_JSON", + Desc: "Output data as json; defaults false", + Default: false, + }) + } + opts.mustRegister(cmd) +} + func setViperOptions() { viper.SetEnvPrefix("INFLUX") viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) } +func writeJSON(w io.Writer, v interface{}) error { + enc := json.NewEncoder(w) + enc.SetIndent("", "\t") + return enc.Encode(v) +} + func newBucketService() (influxdb.BucketService, error) { if flags.local { return newLocalKVService() diff --git a/cmd/influx/pkg.go b/cmd/influx/pkg.go index 1f1f45dbbd1..b1b9a909d04 100644 --- a/cmd/influx/pkg.go +++ b/cmd/influx/pkg.go @@ -97,10 +97,9 @@ func (b *cmdPkgBuilder) cmdPkgApply() *cobra.Command { b.org.register(cmd, false) b.registerPkgFileFlags(cmd) + b.registerPkgPrintOpts(cmd) cmd.Flags().BoolVarP(&b.quiet, "quiet", "q", false, "Disable output printing") cmd.Flags().StringVar(&b.applyOpts.force, "force", "", `TTY input, if package will have destructive changes, proceed if set "true"`) - cmd.Flags().BoolVarP(&b.disableColor, "disable-color", "c", false, "Disable color in output") - cmd.Flags().BoolVar(&b.disableTableBorders, "disable-table-borders", false, "Disable table borders") b.applyOpts.secrets = []string{} cmd.Flags().StringSliceVar(&b.applyOpts.secrets, "secret", nil, "Secrets to provide alongside the package; format should --secret=SECRET_KEY=SECRET_VALUE --secret=SECRET_KEY_2=SECRET_VALUE_2") @@ -155,8 +154,8 @@ func (b *cmdPkgBuilder) pkgApplyRunEFn(cmd *cobra.Command, args []string) error } } - if !b.quiet { - b.printPkgDiff(diff) + if err := b.printPkgDiff(diff); err != nil { + return err } isForced, _ := strconv.ParseBool(b.applyOpts.force) @@ -177,9 +176,7 @@ func (b *cmdPkgBuilder) pkgApplyRunEFn(cmd *cobra.Command, args []string) error return err } - if !b.quiet { - b.printPkgSummary(summary) - } + b.printPkgSummary(summary) return nil } @@ -319,16 +316,14 @@ func (b *cmdPkgBuilder) cmdPkgSummary() *cobra.Command { return err } - b.printPkgSummary(pkg.Summary()) - return nil + return b.printPkgSummary(pkg.Summary()) } cmd := b.newCmd("summary", runE, false) cmd.Short = "Summarize the provided package" b.registerPkgFileFlags(cmd) - cmd.Flags().BoolVarP(&b.disableColor, "disable-color", "c", false, "Disable color in output") - cmd.Flags().BoolVar(&b.disableTableBorders, "disable-table-borders", false, "Disable table borders") + b.registerPkgPrintOpts(cmd) return cmd } @@ -412,6 +407,12 @@ func (b *cmdPkgBuilder) stackInitRunEFn(cmd *cobra.Command, args []string) error return nil } +func (b *cmdPkgBuilder) registerPkgPrintOpts(cmd *cobra.Command) { + cmd.Flags().BoolVarP(&b.disableColor, "disable-color", "c", false, "Disable color in output") + cmd.Flags().BoolVar(&b.disableTableBorders, "disable-table-borders", false, "Disable table borders") + registerPrintOptions(cmd, nil, &b.json) +} + func (b *cmdPkgBuilder) registerPkgFileFlags(cmd *cobra.Command) { cmd.Flags().StringSliceVarP(&b.files, "file", "f", nil, "Path to package file") cmd.MarkFlagFilename("file", "yaml", "yml", "json", "jsonnet") @@ -664,7 +665,15 @@ func newPkgerSVC() (pkger.SVC, influxdb.OrganizationService, error) { return &pkger.HTTPRemoteService{Client: httpClient}, orgSvc, nil } -func (b *cmdPkgBuilder) printPkgDiff(diff pkger.Diff) { +func (b *cmdPkgBuilder) printPkgDiff(diff pkger.Diff) error { + if b.quiet { + return nil + } + + if b.json { + return b.writeJSON(diff) + } + red := color.New(color.FgRed).SprintFunc() green := color.New(color.FgHiGreen, color.Bold).SprintFunc() @@ -853,9 +862,19 @@ func (b *cmdPkgBuilder) printPkgDiff(diff pkger.Diff) { } }) } + + return nil } -func (b *cmdPkgBuilder) printPkgSummary(sum pkger.Summary) { +func (b *cmdPkgBuilder) printPkgSummary(sum pkger.Summary) error { + if b.quiet { + return nil + } + + if b.json { + return b.writeJSON(sum) + } + tablePrintFn := b.tablePrinterGen() if labels := sum.Labels; len(labels) > 0 { headers := []string{"ID", "Name", "Description", "Color"} @@ -997,6 +1016,8 @@ func (b *cmdPkgBuilder) printPkgSummary(sum pkger.Summary) { return []string{secrets[i]} }) } + + return nil } func (b *cmdPkgBuilder) tablePrinterGen() func(table string, headers []string, count int, rowFn func(i int) []string) {