diff --git a/cmd/influx/config.go b/cmd/influx/config.go index 5cf61ee9c29..5bc6ffdeba1 100644 --- a/cmd/influx/config.go +++ b/cmd/influx/config.go @@ -35,6 +35,9 @@ type cmdConfigBuilder struct { active bool org string + json bool + hideHeaders bool + svc config.ConfigsService } @@ -54,6 +57,8 @@ func (b *cmdConfigBuilder) cmd() *cobra.Command { func (b *cmdConfigBuilder) cmdCreate() *cobra.Command { cmd := b.newCmd("create", b.cmdCreateRunEFn, false) cmd.Short = "Create config" + + b.registerPrintFlags(cmd) cmd.Flags().StringVarP(&b.name, "name", "n", "", "The config name (required)") cmd.MarkFlagRequired("name") cmd.Flags().StringVarP(&b.token, "token", "t", "", "The config token (required)") @@ -71,6 +76,7 @@ func (b *cmdConfigBuilder) cmdCreateRunEFn(*cobra.Command, []string) error { if err != nil { return err } + p := config.Config{ Host: b.url, Token: b.token, @@ -83,41 +89,31 @@ func (b *cmdConfigBuilder) cmdCreateRunEFn(*cobra.Command, []string) error { Msg: fmt.Sprintf("name %q already exists", b.name), } } + pp[b.name] = p - active := "" if p.Active { - active = "*" if err := pp.Switch(b.name); err != nil { return err } } + if err = b.svc.WriteConfigs(pp); err != nil { return err } - w := b.newTabWriter() - w.WriteHeaders( - "Active", - "Name", - "URL", - "Org", - "Created", - ) - w.Write(map[string]interface{}{ - "Active": active, - "Name": b.name, - "URL": p.Host, - "Org": p.Org, - "Created": true, + return b.printConfigs(configPrintOpts{ + config: cfg{ + name: b.name, + Config: p, + }, }) - w.Flush() - return nil } func (b *cmdConfigBuilder) cmdDelete() *cobra.Command { cmd := b.newCmd("delete", b.cmdDeleteRunEFn, false) cmd.Short = "Delete config" + b.registerPrintFlags(cmd) cmd.Flags().StringVarP(&b.name, "name", "n", "", "The config name (required)") cmd.MarkFlagRequired("name") @@ -129,6 +125,7 @@ func (b *cmdConfigBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) er if err != nil { return err } + p, ok := pp[b.name] if !ok { return &influxdb.Error{ @@ -137,31 +134,26 @@ func (b *cmdConfigBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) er } } delete(pp, b.name) + if err = b.svc.WriteConfigs(pp); err != nil { return err } - w := b.newTabWriter() - w.WriteHeaders( - "Name", - "URL", - "Org", - "Deleted", - ) - w.Write(map[string]interface{}{ - "Name": b.name, - "URL": p.Host, - "Org": p.Org, - "Deleted": true, + return b.printConfigs(configPrintOpts{ + delete: true, + config: cfg{ + name: b.name, + Config: p, + }, }) - w.Flush() - return nil } func (b *cmdConfigBuilder) cmdUpdate() *cobra.Command { cmd := b.newCmd("set", b.cmdUpdateRunEFn, false) cmd.Aliases = []string{"update"} cmd.Short = "Update config" + + b.registerPrintFlags(cmd) cmd.Flags().StringVarP(&b.name, "name", "n", "", "The config name (required)") cmd.MarkFlagRequired("name") @@ -177,6 +169,7 @@ func (b *cmdConfigBuilder) cmdUpdateRunEFn(*cobra.Command, []string) error { if err != nil { return err } + p0, ok := pp[b.name] if !ok { return &influxdb.Error{ @@ -193,41 +186,31 @@ func (b *cmdConfigBuilder) cmdUpdateRunEFn(*cobra.Command, []string) error { if b.org != "" { p0.Org = b.org } + pp[b.name] = p0 - active := "" if b.active { - active = "*" if err := pp.Switch(b.name); err != nil { return err } } + if err = b.svc.WriteConfigs(pp); err != nil { return err } - w := b.newTabWriter() - w.WriteHeaders( - "Active", - "Name", - "URL", - "Org", - "Updated", - ) - w.Write(map[string]interface{}{ - "Active": active, - "Name": b.name, - "URL": p0.Host, - "Org": p0.Org, - "Updated": true, + return b.printConfigs(configPrintOpts{ + config: cfg{ + name: b.name, + Config: p0, + }, }) - w.Flush() - return nil } func (b *cmdConfigBuilder) cmdList() *cobra.Command { cmd := b.newCmd("list", b.cmdListRunEFn, false) cmd.Aliases = []string{"ls"} cmd.Short = "List configs" + b.registerPrintFlags(cmd) return cmd } @@ -236,26 +219,75 @@ func (b *cmdConfigBuilder) cmdListRunEFn(*cobra.Command, []string) error { if err != nil { return err } - w := b.newTabWriter() - w.WriteHeaders( - "Active", - "Name", - "URL", - "Org", - ) + + var cfgs []cfg for n, p := range pp { + cfgs = append(cfgs, cfg{ + name: n, + Config: p, + }) + } + + return b.printConfigs(configPrintOpts{configs: cfgs}) +} + +func (b *cmdConfigBuilder) registerPrintFlags(cmd *cobra.Command) { + registerPrintOptions(cmd, &b.hideHeaders, &b.json) +} + +func (b *cmdConfigBuilder) printConfigs(opts configPrintOpts) error { + if b.json { + var v interface{} = opts.configs + if opts.configs == nil { + v = opts.config + } + return b.writeJSON(v) + } + + w := b.newTabWriter() + defer w.Flush() + + w.HideHeaders(b.hideHeaders) + + headers := []string{"Active", "Name", "URL", "Org"} + if opts.delete { + headers = append(headers, "Deleted") + } + w.WriteHeaders(headers...) + + if opts.configs == nil { + opts.configs = append(opts.configs, opts.config) + } + for _, c := range opts.configs { var active string - if p.Active { + if c.Active { active = "*" } - w.Write(map[string]interface{}{ + m := map[string]interface{}{ "Active": active, - "Name": n, - "URL": p.Host, - "Org": p.Org, - }) + "Name": c.name, + "URL": c.Host, + "Org": c.Org, + } + if opts.delete { + m["Deleted"] = true + } + + w.Write(m) } - w.Flush() return nil } + +type ( + configPrintOpts struct { + delete bool + config cfg + configs []cfg + } + + cfg struct { + name string + config.Config + } +) diff --git a/cmd/influx/config/config.go b/cmd/influx/config/config.go index 8882acd8c34..f66803ca0a5 100644 --- a/cmd/influx/config/config.go +++ b/cmd/influx/config/config.go @@ -14,11 +14,11 @@ import ( // Config store the crendentials of influxdb host and token. type Config struct { - Host string `toml:"url"` + Host string `toml:"url" json:"url"` // Token is base64 encoded sequence. - Token string `toml:"token"` - Org string `toml:"org,omitempty"` - Active bool `toml:"active,omitempty"` + Token string `toml:"token" json:"token"` + Org string `toml:"org" json:"org"` + Active bool `toml:"active" json:"active"` } // DefaultConfig is default config without token diff --git a/cmd/influx/organization.go b/cmd/influx/organization.go index 40255826d4c..2d5fe7ec97f 100644 --- a/cmd/influx/organization.go +++ b/cmd/influx/organization.go @@ -25,6 +25,8 @@ type cmdOrgBuilder struct { svcFn orgSVCFn + json bool + hideHeaders bool description string id string memberID string @@ -59,6 +61,7 @@ func (b *cmdOrgBuilder) cmdCreate() *cobra.Command { cmd := b.newCmd("create", b.createRunEFn, true) cmd.Short = "Create organization" + b.registerPrintFlags(cmd) cmd.Flags().StringVarP(&b.name, "name", "n", "", "The name of organization that will be created") cmd.MarkFlagRequired("name") cmd.Flags().StringVarP(&b.description, "description", "d", "", "The description of the organization that will be created") @@ -81,15 +84,7 @@ func (b *cmdOrgBuilder) createRunEFn(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to create organization: %v", err) } - w := b.newTabWriter() - w.WriteHeaders("ID", "Name") - w.Write(map[string]interface{}{ - "ID": org.ID.String(), - "Name": org.Name, - }) - w.Flush() - - return nil + return b.printOrg(orgPrintOpt{org: org}) } func (b *cmdOrgBuilder) cmdDelete() *cobra.Command { @@ -106,6 +101,7 @@ func (b *cmdOrgBuilder) cmdDelete() *cobra.Command { }, } opts.mustRegister(cmd) + b.registerPrintFlags(cmd) return cmd } @@ -131,16 +127,10 @@ func (b *cmdOrgBuilder) deleteRunEFn(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to delete org with id %q: %v", id, err) } - w := b.newTabWriter() - w.WriteHeaders("ID", "Name", "Deleted") - w.Write(map[string]interface{}{ - "ID": o.ID.String(), - "Name": o.Name, - "Deleted": true, + return b.printOrg(orgPrintOpt{ + deleted: true, + org: o, }) - w.Flush() - - return nil } func (b *cmdOrgBuilder) cmdFind() *cobra.Command { @@ -165,6 +155,7 @@ func (b *cmdOrgBuilder) cmdFind() *cobra.Command { }, } opts.mustRegister(cmd) + b.registerPrintFlags(cmd) return cmd } @@ -193,17 +184,7 @@ func (b *cmdOrgBuilder) findRunEFn(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed find orgs: %v", err) } - w := b.newTabWriter() - w.WriteHeaders("ID", "Name") - for _, o := range orgs { - w.Write(map[string]interface{}{ - "ID": o.ID.String(), - "Name": o.Name, - }) - } - w.Flush() - - return nil + return b.printOrg(orgPrintOpt{orgs: orgs}) } func (b *cmdOrgBuilder) cmdUpdate() *cobra.Command { @@ -235,6 +216,7 @@ func (b *cmdOrgBuilder) cmdUpdate() *cobra.Command { }, } opts.mustRegister(cmd) + b.registerPrintFlags(cmd) return cmd } @@ -263,13 +245,43 @@ func (b *cmdOrgBuilder) updateRunEFn(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to update org: %v", err) } + return b.printOrg(orgPrintOpt{org: o}) +} + +func (b *cmdOrgBuilder) printOrg(opts orgPrintOpt) error { + if b.json { + var v interface{} = opts.orgs + if opts.org != nil { + v = opts.org + } + return b.writeJSON(v) + } + w := b.newTabWriter() - w.WriteHeaders("ID", "Name") - w.Write(map[string]interface{}{ - "ID": o.ID.String(), - "Name": o.Name, - }) - w.Flush() + defer w.Flush() + + w.HideHeaders(b.hideHeaders) + + headers := []string{"ID", "Name"} + if opts.deleted { + headers = append(headers, "Deleted") + } + w.WriteHeaders(headers...) + + if opts.org != nil { + opts.orgs = append(opts.orgs, opts.org) + } + + for _, o := range opts.orgs { + m := map[string]interface{}{ + "ID": o.ID.String(), + "Name": o.Name, + } + if opts.deleted { + m["Deleted"] = true + } + w.Write(m) + } return nil } @@ -343,7 +355,7 @@ func (b *cmdOrgBuilder) memberListRunEFn(cmd *cobra.Command, args []string) erro } ctx := context.Background() - return memberList(ctx, b, urmSVC, userSVC, influxdb.UserResourceMappingFilter{ + return b.memberList(ctx, urmSVC, userSVC, influxdb.UserResourceMappingFilter{ ResourceType: influxdb.OrgsResourceType, ResourceID: organization.ID, UserType: influxdb.Member, @@ -497,6 +509,10 @@ func (b *cmdOrgBuilder) membersRemoveRunEFn(cmd *cobra.Command, args []string) e return removeMember(ctx, b.w, urmSVC, organization.ID, memberID) } +func (b *cmdOrgBuilder) registerPrintFlags(cmd *cobra.Command) { + registerPrintOptions(cmd, &b.hideHeaders, &b.json) +} + func newOrgServices() (influxdb.OrganizationService, influxdb.UserResourceMappingService, influxdb.UserService, error) { if flags.local { svc, err := newLocalKVService() @@ -533,20 +549,21 @@ func newOrganizationService() (influxdb.OrganizationService, error) { }, nil } -func memberList(ctx context.Context, b *cmdOrgBuilder, urmSVC influxdb.UserResourceMappingService, userSVC influxdb.UserService, f influxdb.UserResourceMappingFilter) error { - mps, _, err := urmSVC.FindUserResourceMappings(ctx, f) +func (b *cmdOrgBuilder) memberList(ctx context.Context, urmSVC influxdb.UserResourceMappingService, userSVC influxdb.UserService, f influxdb.UserResourceMappingFilter) error { + mappings, _, err := urmSVC.FindUserResourceMappings(ctx, f) if err != nil { return fmt.Errorf("failed to find members: %v", err) } - urs := make([]*influxdb.User, len(mps)) - ursC := make(chan struct { - User *influxdb.User - Index int - }) - errC := make(chan error) - sem := make(chan struct{}, maxTCPConnections) - for k, v := range mps { + var ( + ursC = make(chan struct { + User *influxdb.User + Index int + }) + errC = make(chan error) + sem = make(chan struct{}, maxTCPConnections) + ) + for k, v := range mappings { sem <- struct{}{} go func(k int, v *influxdb.UserResourceMapping) { defer func() { <-sem }() @@ -564,7 +581,9 @@ func memberList(ctx context.Context, b *cmdOrgBuilder, urmSVC influxdb.UserResou } }(k, v) } - for i := 0; i < len(mps); i++ { + + users := make([]*influxdb.User, len(mappings)) + for i := 0; i < len(mappings); i++ { select { case <-ctx.Done(): return &influxdb.Error{ @@ -573,20 +592,29 @@ func memberList(ctx context.Context, b *cmdOrgBuilder, urmSVC influxdb.UserResou case err := <-errC: return err case item := <-ursC: - urs[item.Index] = item.User + users[item.Index] = item.User } } + if b.json { + return b.writeJSON(users) + } + tw := b.newTabWriter() - tw.WriteHeaders("ID", "Name", "Status") - for _, m := range urs { + defer tw.Flush() + + tw.HideHeaders(b.hideHeaders) + + tw.WriteHeaders("ID", "Name", "User Type", "Status") + for idx, m := range users { tw.Write(map[string]interface{}{ - "ID": m.ID.String(), - "Name": m.Name, - "Status": string(m.Status), + "ID": m.ID.String(), + "User Name": m.Name, + "User Type": string(mappings[idx].UserType), + "Status": string(m.Status), }) } - tw.Flush() + return nil } @@ -605,3 +633,9 @@ func removeMember(ctx context.Context, w io.Writer, urmSVC influxdb.UserResource _, err := fmt.Fprintf(w, "userID %s has been removed from ResourceID %s\n", userID, resourceID) return err } + +type orgPrintOpt struct { + deleted bool + org *influxdb.Organization + orgs []*influxdb.Organization +} diff --git a/cmd/influx/secret.go b/cmd/influx/secret.go index 62dd1e23ba0..ad34335024a 100644 --- a/cmd/influx/secret.go +++ b/cmd/influx/secret.go @@ -24,9 +24,11 @@ type cmdSecretBuilder struct { svcFn secretSVCsFn - key string - value string - org organization + json bool + hideHeaders bool + key string + value string + org organization } func newCmdSecretBuilder(svcsFn secretSVCsFn, opt genericCLIOpts) *cmdSecretBuilder { @@ -51,21 +53,12 @@ func (b *cmdSecretBuilder) cmd() *cobra.Command { func (b *cmdSecretBuilder) cmdUpdate() *cobra.Command { cmd := b.newCmd("update", b.cmdUpdateRunEFn, true) cmd.Short = "Update secret" - cmd.Flags().StringVarP(&b.key, "key", "k", "", "The secret key (required)") - cmd.Flags().StringVarP(&b.value, "value", "v", "", "Optional secret value for scripting convenience, using this might expose the secret to your local history") - cmd.MarkFlagRequired("key") - b.org.register(cmd, false) - - return cmd -} - -func (b *cmdSecretBuilder) cmdDelete() *cobra.Command { - cmd := b.newCmd("delete", b.cmdDeleteRunEFn, true) - cmd.Short = "Delete secret" cmd.Flags().StringVarP(&b.key, "key", "k", "", "The secret key (required)") + cmd.Flags().StringVarP(&b.value, "value", "v", "", "Optional secret value for scripting convenience, using this might expose the secret to your local history") cmd.MarkFlagRequired("key") b.org.register(cmd, false) + b.registerPrintFlags(cmd) return cmd } @@ -86,27 +79,38 @@ func (b *cmdSecretBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) er Writer: b.genericCLIOpts.w, Reader: b.genericCLIOpts.in, } - var secret string + var secretVal string if b.value != "" { - secret = b.value + secretVal = b.value } else { - secret = getSecretFn(ui) + secretVal = getSecretFn(ui) } - if err := scrSVC.PatchSecrets(ctx, orgID, map[string]string{b.key: secret}); err != nil { + err = scrSVC.PatchSecrets(ctx, orgID, map[string]string{ + b.key: secretVal, + }) + if err != nil { return fmt.Errorf("failed to update secret with key %q: %v", b.key, err) } - w := b.newTabWriter() - w.WriteHeaders("Key", "OrgID", "Updated") - w.Write(map[string]interface{}{ - "Key": b.key, - "OrgID": orgID, - "Updated": true, + return b.printSecrets(secretPrintOpt{ + secret: secret{ + key: b.key, + orgID: orgID, + }, }) - w.Flush() +} - return nil +func (b *cmdSecretBuilder) cmdDelete() *cobra.Command { + cmd := b.newCmd("delete", b.cmdDeleteRunEFn, true) + cmd.Short = "Delete secret" + + cmd.Flags().StringVarP(&b.key, "key", "k", "", "The secret key (required)") + cmd.MarkFlagRequired("key") + b.org.register(cmd, false) + b.registerPrintFlags(cmd) + + return cmd } func (b *cmdSecretBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) error { @@ -124,29 +128,27 @@ func (b *cmdSecretBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) er return fmt.Errorf("failed to delete secret with key %q: %v", b.key, err) } - w := b.newTabWriter() - w.WriteHeaders("Key", "OrgID", "Deleted") - w.Write(map[string]interface{}{ - "Key": b.key, - "OrgID": orgID, - "Deleted": true, + return b.printSecrets(secretPrintOpt{ + deleted: true, + secret: secret{ + key: b.key, + orgID: orgID, + }, }) - w.Flush() - - return nil } func (b *cmdSecretBuilder) cmdFind() *cobra.Command { cmd := b.newCmd("list", b.cmdFindRunEFn, true) cmd.Short = "List secrets" cmd.Aliases = []string{"find", "ls"} + b.org.register(cmd, false) + b.registerPrintFlags(cmd) return cmd } func (b *cmdSecretBuilder) cmdFindRunEFn(cmd *cobra.Command, args []string) error { - scrSVC, orgSVC, _, err := b.svcFn() if err != nil { return err @@ -157,24 +159,79 @@ func (b *cmdSecretBuilder) cmdFindRunEFn(cmd *cobra.Command, args []string) erro return err } - secrets, err := scrSVC.GetSecretKeys(context.Background(), orgID) + platformSecrets, err := scrSVC.GetSecretKeys(context.Background(), orgID) if err != nil { return fmt.Errorf("failed to retrieve secret keys: %s", err) } - w := b.newTabWriter() - w.WriteHeaders("Key", "OrganizationID") - for _, s := range secrets { - w.Write(map[string]interface{}{ - "Key": s, - "OrganizationID": orgID, + secrets := make([]secret, 0, len(platformSecrets)) + for _, key := range platformSecrets { + secrets = append(secrets, secret{ + key: key, + orgID: orgID, }) } - w.Flush() + + return b.printSecrets(secretPrintOpt{ + secrets: secrets, + }) +} + +func (b *cmdSecretBuilder) registerPrintFlags(cmd *cobra.Command) { + registerPrintOptions(cmd, &b.hideHeaders, &b.json) +} + +func (b *cmdSecretBuilder) printSecrets(opt secretPrintOpt) error { + if b.json { + var v interface{} = opt.secrets + if opt.secrets == nil { + v = opt.secret + } + return b.writeJSON(v) + } + + w := b.newTabWriter() + defer w.Flush() + + w.HideHeaders(b.hideHeaders) + + headers := []string{"Key", "Organization ID"} + if opt.deleted { + headers = append(headers, "Deleted") + } + w.WriteHeaders(headers...) + + if opt.secrets == nil { + opt.secrets = append(opt.secrets, opt.secret) + } + + for _, s := range opt.secrets { + m := map[string]interface{}{ + "Key": s.key, + "Organization ID": s.orgID.String(), + } + if opt.deleted { + m["Deleted"] = true + } + w.Write(m) + } return nil } +type ( + secretPrintOpt struct { + deleted bool + secret secret + secrets []secret + } + + secret struct { + key string + orgID influxdb.ID + } +) + func newSecretSVCs() (influxdb.SecretService, influxdb.OrganizationService, func(*input.UI) string, error) { httpClient, err := newHTTPClient() if err != nil { diff --git a/cmd/influx/setup.go b/cmd/influx/setup.go index 29883baf007..4033dbceae3 100644 --- a/cmd/influx/setup.go +++ b/cmd/influx/setup.go @@ -18,14 +18,16 @@ import ( ) var setupFlags struct { - username string - password string - token string - org string - bucket string - retention time.Duration - name string - force bool + bucket string + force bool + hideHeaders bool + json bool + name string + org string + password string + retention time.Duration + token string + username string } func cmdSetup(f *globalFlags, opt genericCLIOpts) *cobra.Command { @@ -41,6 +43,7 @@ func cmdSetup(f *globalFlags, opt genericCLIOpts) *cobra.Command { cmd.Flags().StringVarP(&setupFlags.name, "name", "n", "", "config name, only required if you already have existing configs") cmd.Flags().DurationVarP(&setupFlags.retention, "retention", "r", -1, "Duration bucket will retain data. 0 is infinite. Default is 0.") cmd.Flags().BoolVarP(&setupFlags.force, "force", "f", false, "skip confirmation prompt") + registerPrintOptions(cmd, &setupFlags.hideHeaders, &setupFlags.json) return cmd } @@ -120,20 +123,27 @@ func setupF(cmd *cobra.Command, args []string) error { fmt.Println(string(promptWithColor(fmt.Sprintf("Config %s has been stored in %s.", configName, dPath), colorCyan))) - w := internal.NewTabWriter(os.Stdout) - w.WriteHeaders( - "User", - "Organization", - "Bucket", - ) - w.Write(map[string]interface{}{ + w := cmd.OutOrStdout() + if setupFlags.json { + return writeJSON(w, map[string]interface{}{ + "user": result.User.Name, + "organization": result.Org.Name, + "bucket": result.Bucket.Name, + }) + } + + tabW := internal.NewTabWriter(w) + defer tabW.Flush() + + tabW.HideHeaders(setupFlags.hideHeaders) + + tabW.WriteHeaders("User", "Organization", "Bucket") + tabW.Write(map[string]interface{}{ "User": result.User.Name, "Organization": result.Org.Name, "Bucket": result.Bucket.Name, }) - w.Flush() - return nil }