diff --git a/cmd/influx/authorization.go b/cmd/influx/authorization.go index 7fd9571ee3e..9e77e01aecd 100644 --- a/cmd/influx/authorization.go +++ b/cmd/influx/authorization.go @@ -8,13 +8,29 @@ import ( "github.com/influxdata/influxdb/cmd/influx/internal" "github.com/influxdata/influxdb/http" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -// AuthorizationCreateFlags are command line args used when creating a authorization -type AuthorizationCreateFlags struct { +func cmdAuth() *cobra.Command { + cmd := &cobra.Command{ + Use: "auth", + Aliases: []string{"authorization"}, + Short: "Authorization management commands", + Run: seeHelp, + } + cmd.AddCommand( + authActiveCmd(), + authCreateCmd(), + authDeleteCmd(), + authFindCmd(), + authInactiveCmd(), + ) + + return cmd +} + +var authCreateFlags struct { user string - org string + organization writeUserPermission bool readUserPermission bool @@ -47,39 +63,13 @@ type AuthorizationCreateFlags struct { readNotificationEndpointPermission bool } -var authCreateFlags AuthorizationCreateFlags - -func authCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "auth", - Aliases: []string{"authorization"}, - Short: "Authorization management commands", - Run: seeHelp, - } - cmd.AddCommand( - authActiveCmd(), - authCreateCmd(), - authDeleteCmd(), - authFindCmd(), - authInactiveCmd(), - ) - - return cmd -} - func authCreateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "Create authorization", RunE: wrapCheckSetup(authorizationCreateF), } - - cmd.Flags().StringVarP(&authCreateFlags.org, "org", "o", "", "The organization name (required)") - cmd.MarkFlagRequired("org") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - authCreateFlags.org = h - } + authCreateFlags.organization.register(cmd, false) cmd.Flags().StringVarP(&authCreateFlags.user, "user", "u", "", "The user name") @@ -117,15 +107,16 @@ func authCreateCmd() *cobra.Command { } func authorizationCreateF(cmd *cobra.Command, args []string) error { - var permissions []platform.Permission + if err := authCreateFlags.organization.validOrgFlags(); err != nil { + return err + } + orgSvc, err := newOrganizationService() if err != nil { return err } - ctx := context.Background() - orgFilter := platform.OrganizationFilter{Name: &authCreateFlags.org} - o, err := orgSvc.FindOrganization(ctx, orgFilter) + orgID, err := authCreateFlags.organization.getID(orgSvc) if err != nil { return err } @@ -138,6 +129,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { {action: platform.WriteAction, perms: authCreateFlags.writeBucketPermissions}, } + var permissions []platform.Permission for _, bp := range bucketPerms { for _, p := range bp.perms { var id platform.ID @@ -145,7 +137,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { return err } - p, err := platform.NewPermissionAtID(id, bp.action, platform.BucketsResourceType, o.ID) + p, err := platform.NewPermissionAtID(id, bp.action, platform.BucketsResourceType, orgID) if err != nil { return err } @@ -216,7 +208,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { } for _, action := range actions { - p, err := platform.NewPermission(action, provided.ResourceType, o.ID) + p, err := platform.NewPermission(action, provided.ResourceType, orgID) if err != nil { return err } @@ -226,7 +218,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { authorization := &platform.Authorization{ Permissions: permissions, - OrgID: o.ID, + OrgID: orgID, } if userName := authCreateFlags.user; userName != "" { @@ -234,7 +226,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { if err != nil { return err } - user, err := userSvc.FindUser(ctx, platform.UserFilter{ + user, err := userSvc.FindUser(context.Background(), platform.UserFilter{ Name: &userName, }) if err != nil { @@ -243,7 +235,7 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { authorization.UserID = user.ID } - s, err := newAuthorizationService(flags) + s, err := newAuthorizationService() if err != nil { return err } @@ -279,17 +271,13 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { return nil } -// AuthorizationFindFlags are command line args used when finding a authorization -type AuthorizationFindFlags struct { +var authorizationFindFlags struct { + organization user string userID string - org string - orgID string id string } -var authorizationFindFlags AuthorizationFindFlags - func authFindCmd() *cobra.Command { cmd := &cobra.Command{ Use: "find", @@ -299,23 +287,13 @@ func authFindCmd() *cobra.Command { cmd.Flags().StringVarP(&authorizationFindFlags.user, "user", "u", "", "The user") cmd.Flags().StringVarP(&authorizationFindFlags.userID, "user-id", "", "", "The user ID") - cmd.Flags().StringVarP(&authorizationFindFlags.org, "org", "o", "", "The org") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - authorizationFindFlags.org = h - } - cmd.Flags().StringVarP(&authorizationFindFlags.orgID, "org-id", "", "", "The org ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - authorizationFindFlags.orgID = h - } cmd.Flags().StringVarP(&authorizationFindFlags.id, "id", "i", "", "The authorization ID") return cmd } -func newAuthorizationService(f Flags) (platform.AuthorizationService, error) { +func newAuthorizationService() (platform.AuthorizationService, error) { if flags.local { return newLocalKVService() } @@ -331,7 +309,7 @@ func newAuthorizationService(f Flags) (platform.AuthorizationService, error) { } func authorizationFindF(cmd *cobra.Command, args []string) error { - s, err := newAuthorizationService(flags) + s, err := newAuthorizationService() if err != nil { return err } @@ -354,11 +332,11 @@ func authorizationFindF(cmd *cobra.Command, args []string) error { } filter.UserID = uID } - if authorizationFindFlags.org != "" { - filter.Org = &authorizationFindFlags.org + if authorizationFindFlags.organization.name != "" { + filter.Org = &authorizationFindFlags.organization.name } - if authorizationFindFlags.orgID != "" { - oID, err := platform.IDFromString(authorizationFindFlags.orgID) + if authorizationFindFlags.organization.id != "" { + oID, err := platform.IDFromString(authorizationFindFlags.organization.id) if err != nil { return err } @@ -400,13 +378,10 @@ func authorizationFindF(cmd *cobra.Command, args []string) error { return nil } -// AuthorizationDeleteFlags are command line args used when deleting a authorization -type AuthorizationDeleteFlags struct { +var authorizationDeleteFlags struct { id string } -var authorizationDeleteFlags AuthorizationDeleteFlags - func authDeleteCmd() *cobra.Command { cmd := &cobra.Command{ Use: "delete", @@ -421,7 +396,7 @@ func authDeleteCmd() *cobra.Command { } func authorizationDeleteF(cmd *cobra.Command, args []string) error { - s, err := newAuthorizationService(flags) + s, err := newAuthorizationService() if err != nil { return err } @@ -469,13 +444,10 @@ func authorizationDeleteF(cmd *cobra.Command, args []string) error { return nil } -// AuthorizationActiveFlags are command line args used when enabling an authorization -type AuthorizationActiveFlags struct { +var authorizationActiveFlags struct { id string } -var authorizationActiveFlags AuthorizationActiveFlags - func authActiveCmd() *cobra.Command { cmd := &cobra.Command{ Use: "active", @@ -490,7 +462,7 @@ func authActiveCmd() *cobra.Command { } func authorizationActiveF(cmd *cobra.Command, args []string) error { - s, err := newAuthorizationService(flags) + s, err := newAuthorizationService() if err != nil { return err } @@ -540,13 +512,10 @@ func authorizationActiveF(cmd *cobra.Command, args []string) error { return nil } -// AuthorizationInactiveFlags are command line args used when disabling an authorization -type AuthorizationInactiveFlags struct { +var authorizationInactiveFlags struct { id string } -var authorizationInactiveFlags AuthorizationInactiveFlags - func authInactiveCmd() *cobra.Command { cmd := &cobra.Command{ Use: "inactive", @@ -561,7 +530,7 @@ func authInactiveCmd() *cobra.Command { } func authorizationInactiveF(cmd *cobra.Command, args []string) error { - s, err := newAuthorizationService(flags) + s, err := newAuthorizationService() if err != nil { return err } diff --git a/cmd/influx/bucket.go b/cmd/influx/bucket.go index b3805c0ae13..3e3fdabddb8 100644 --- a/cmd/influx/bucket.go +++ b/cmd/influx/bucket.go @@ -10,43 +10,48 @@ import ( "github.com/influxdata/influxdb/cmd/influx/internal" "github.com/influxdata/influxdb/http" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -// Bucket Command -var bucketCmd = &cobra.Command{ - Use: "bucket", - Short: "Bucket management commands", - TraverseChildren: true, - Run: seeHelp, +func cmdBucket() *cobra.Command { + cmd := &cobra.Command{ + Use: "bucket", + Short: "Bucket management commands", + TraverseChildren: true, + Run: seeHelp, + } + cmd.AddCommand( + bucketCreateCmd(), + bucketDeleteCmd(), + bucketFindCmd(), + bucketUpdateCmd(), + ) + + return cmd } -// BucketCreateFlags define the Create Command -type BucketCreateFlags struct { +var bucketCreateFlags struct { name string organization retention time.Duration } -var bucketCreateFlags BucketCreateFlags - -func init() { - bucketCreateCmd := &cobra.Command{ +func bucketCreateCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "create", Short: "Create bucket", RunE: wrapCheckSetup(bucketCreateF), } - bucketCreateCmd.Flags().StringVarP(&bucketCreateFlags.name, "name", "n", "", "Name of bucket that will be created") - bucketCreateCmd.Flags().DurationVarP(&bucketCreateFlags.retention, "retention", "r", 0, "Duration in nanoseconds data will live in bucket") - bucketCreateCmd.MarkFlagRequired("name") - bucketCreateFlags.organization.register(bucketCreateCmd) + cmd.Flags().StringVar(&bucketCreateFlags.name, "name", "n", "Name of bucket that will be created") + cmd.MarkFlagRequired("name") + cmd.Flags().DurationVarP(&bucketCreateFlags.retention, "retention", "r", 0, "Duration in nanoseconds data will live in bucket") + bucketCreateFlags.organization.register(cmd, false) - bucketCmd.AddCommand(bucketCreateCmd) + return cmd } -func newBucketService(f Flags) (platform.BucketService, error) { - if f.local { +func newBucketService() (platform.BucketService, error) { + if flags.local { return newLocalKVService() } @@ -65,7 +70,7 @@ func bucketCreateF(cmd *cobra.Command, args []string) error { return err } - s, err := newBucketService(flags) + s, err := newBucketService() if err != nil { return fmt.Errorf("failed to initialize bucket service client: %v", err) } @@ -107,37 +112,40 @@ func bucketCreateF(cmd *cobra.Command, args []string) error { return nil } -// BucketFindFlags define the Find Command -type BucketFindFlags struct { +var bucketFindFlags struct { name string id string headers bool organization } -var bucketFindFlags BucketFindFlags - -func init() { - bucketFindCmd := &cobra.Command{ +func bucketFindCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "find", Short: "Find buckets", RunE: wrapCheckSetup(bucketFindF), } - bucketFindCmd.Flags().StringVarP(&bucketFindFlags.name, "name", "n", "", "The bucket name") - viper.BindEnv("BUCKET_NAME") - if h := viper.GetString("BUCKET_NAME"); h != "" { - bucketFindFlags.name = h + opts := flagOpts{ + { + DestP: &bucketFindFlags.name, + Flag: "name", + Short: 'n', + EnvVar: "BUCKET_NAME", + Desc: "The bucket name", + }, } - bucketFindCmd.Flags().StringVarP(&bucketFindFlags.id, "id", "i", "", "The bucket ID") - bucketFindCmd.Flags().BoolVar(&bucketFindFlags.headers, "headers", true, "To print the table headers; defaults true") - bucketFindFlags.organization.register(bucketFindCmd) + opts.mustRegister(cmd) + + cmd.Flags().StringVarP(&bucketFindFlags.id, "id", "i", "", "The bucket ID") + cmd.Flags().BoolVar(&bucketFindFlags.headers, "headers", true, "To print the table headers; defaults true") + bucketFindFlags.organization.register(cmd, false) - bucketCmd.AddCommand(bucketFindCmd) + return cmd } func bucketFindF(cmd *cobra.Command, args []string) error { - s, err := newBucketService(flags) + s, err := newBucketService() if err != nil { return fmt.Errorf("failed to initialize bucket service client: %v", err) } @@ -197,37 +205,39 @@ func bucketFindF(cmd *cobra.Command, args []string) error { return nil } -// BucketUpdateFlags define the Update Command -type BucketUpdateFlags struct { +var bucketUpdateFlags struct { id string name string retention time.Duration } -var bucketUpdateFlags BucketUpdateFlags - -func init() { - bucketUpdateCmd := &cobra.Command{ +func bucketUpdateCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "update", Short: "Update bucket", RunE: wrapCheckSetup(bucketUpdateF), } - bucketUpdateCmd.Flags().StringVarP(&bucketUpdateFlags.id, "id", "i", "", "The bucket ID (required)") - bucketUpdateCmd.Flags().StringVarP(&bucketUpdateFlags.name, "name", "n", "", "New bucket name") - viper.BindEnv("BUCKET_NAME") - if h := viper.GetString("BUCKET_NAME"); h != "" { - bucketFindFlags.name = h + opts := flagOpts{ + { + DestP: &bucketUpdateFlags.name, + Flag: "name", + Short: 'n', + EnvVar: "BUCKET_NAME", + Desc: "New bucket name", + }, } + opts.mustRegister(cmd) - bucketUpdateCmd.Flags().DurationVarP(&bucketUpdateFlags.retention, "retention", "r", 0, "New duration data will live in bucket") - bucketUpdateCmd.MarkFlagRequired("id") + cmd.Flags().StringVarP(&bucketUpdateFlags.id, "id", "i", "", "The bucket ID (required)") + cmd.MarkFlagRequired("id") + cmd.Flags().DurationVarP(&bucketUpdateFlags.retention, "retention", "r", 0, "New duration data will live in bucket") - bucketCmd.AddCommand(bucketUpdateCmd) + return cmd } func bucketUpdateF(cmd *cobra.Command, args []string) error { - s, err := newBucketService(flags) + s, err := newBucketService() if err != nil { return fmt.Errorf("failed to initialize bucket service client: %v", err) } @@ -268,15 +278,12 @@ func bucketUpdateF(cmd *cobra.Command, args []string) error { return nil } -// BucketDeleteFlags define the Delete command -type BucketDeleteFlags struct { +var bucketDeleteFlags struct { id string } -var bucketDeleteFlags BucketDeleteFlags - func bucketDeleteF(cmd *cobra.Command, args []string) error { - s, err := newBucketService(flags) + s, err := newBucketService() if err != nil { return fmt.Errorf("failed to initialize bucket service client: %v", err) } @@ -316,15 +323,15 @@ func bucketDeleteF(cmd *cobra.Command, args []string) error { return nil } -func init() { - bucketDeleteCmd := &cobra.Command{ +func bucketDeleteCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "delete", Short: "Delete bucket", RunE: wrapCheckSetup(bucketDeleteF), } - bucketDeleteCmd.Flags().StringVarP(&bucketDeleteFlags.id, "id", "i", "", "The bucket ID (required)") - bucketDeleteCmd.MarkFlagRequired("id") + cmd.Flags().StringVar(&bucketDeleteFlags.id, "id", "i", "The bucket ID (required)") + cmd.MarkFlagRequired("id") - bucketCmd.AddCommand(bucketDeleteCmd) + return cmd } diff --git a/cmd/influx/debug.go b/cmd/influx/debug.go index e2fff460c6e..686890812c0 100644 --- a/cmd/influx/debug.go +++ b/cmd/influx/debug.go @@ -1,15 +1,124 @@ package main import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb/internal/fs" + "github.com/influxdata/influxdb/tsdb/tsm1" "github.com/spf13/cobra" ) -// Debug Command -var debugCmd = &cobra.Command{ - Use: "debug", - Short: "commands for debugging InfluxDB", +func debugCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "debug", + Short: "commands for debugging InfluxDB", + } + cmd.AddCommand(initInspectReportTSMCommand()) // Add report-tsm command + + return cmd +} + +var inspectReportTSMFlags struct { + pattern string + exact bool + detailed bool + organization + bucketID string + dataDir string +} + +func initInspectReportTSMCommand() *cobra.Command { + inspectReportTSMCommand := &cobra.Command{ + Use: "report-tsm", + Short: "Run a TSM report", + Long: `This command will analyze TSM files within a storage engine +directory, reporting the cardinality within the files as well as the time range that +the point data covers. + +This command only interrogates the index within each file, and does not read any +block data. To reduce heap requirements, by default report-tsm estimates the overall +cardinality in the file set by using the HLL++ algorithm. Exact cardinalities can +be determined by using the --exact flag. + +For each file, the following is output: + + * The full filename; + * The series cardinality within the file; + * The number of series first encountered within the file; + * The minimum and maximum timestamp associated with any TSM data in the file; and + * The time taken to load the TSM index and apply any tombstones. + +The summary section then outputs the total time range and series cardinality for +the fileset. Depending on the --detailed flag, series cardinality is segmented +in the following ways: + + * Series cardinality for each organization; + * Series cardinality for each bucket; + * Series cardinality for each measurement; + * Number of field keys for each measurement; and + * Number of tag values for each tag key. +`, + RunE: inspectReportTSMF, + } + + inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.pattern, "pattern", "", "", "only process TSM files containing pattern") + inspectReportTSMCommand.Flags().BoolVarP(&inspectReportTSMFlags.exact, "exact", "", false, "calculate and exact cardinality count. Warning, may use significant memory...") + inspectReportTSMCommand.Flags().BoolVarP(&inspectReportTSMFlags.detailed, "detailed", "", false, "emit series cardinality segmented by measurements, tag keys and fields. Warning, may take a while.") + + inspectReportTSMFlags.organization.register(inspectReportTSMCommand, false) + inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.bucketID, "bucket-id", "", "", "process only data belonging to bucket ID. Requires org flag to be set.") + + dir, err := fs.InfluxDir() + if err != nil { + panic(err) + } + inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.dataDir, "data-dir", "", "", fmt.Sprintf("use provided data directory (defaults to %s).", filepath.Join(dir, "engine/data"))) + return inspectReportTSMCommand } -func init() { - debugCmd.AddCommand(initInspectReportTSMCommand()) // Add report-tsm command +// inspectReportTSMF runs the report-tsm tool. +func inspectReportTSMF(cmd *cobra.Command, args []string) error { + if err := inspectReportTSMFlags.organization.validOrgFlags(); err != nil { + return err + } + report := &tsm1.Report{ + Stderr: os.Stderr, + Stdout: os.Stdout, + Dir: inspectReportTSMFlags.dataDir, + Pattern: inspectReportTSMFlags.pattern, + Detailed: inspectReportTSMFlags.detailed, + Exact: inspectReportTSMFlags.exact, + } + + if (inspectReportTSMFlags.organization.name == "" || inspectReportTSMFlags.organization.id == "") && inspectReportTSMFlags.bucketID != "" { + return errors.New("org-id must be set for non-empty bucket-id") + } + + orgSvc, err := newOrganizationService() + if err != nil { + return err + } + id, err := inspectReportTSMFlags.organization.getID(orgSvc) + if err != nil { + return err + } + report.OrgID = &id + + if inspectReportTSMFlags.bucketID != "" { + bucketID, err := influxdb.IDFromString(inspectReportTSMFlags.bucketID) + if err != nil { + return err + } + report.BucketID = bucketID + } + + _, err = report.Run(true) + if err != nil { + panic(err) + } + return err } diff --git a/cmd/influx/delete.go b/cmd/influx/delete.go index e32ae415f3d..bb4c60f85cf 100644 --- a/cmd/influx/delete.go +++ b/cmd/influx/delete.go @@ -7,47 +7,54 @@ import ( "github.com/influxdata/influxdb/http" "github.com/influxdata/influxdb/kit/signals" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var deleteCmd = &cobra.Command{ - Use: "delete points from an influxDB bucket", - Short: "Delete points from influxDB", - Long: `Delete points from influxDB, by specify start, end time - and a sql like predicate string.`, - RunE: wrapCheckSetup(fluxDeleteF), -} - var deleteFlags http.DeleteRequest -func init() { - deleteCmd.PersistentFlags().StringVar(&deleteFlags.OrgID, "org-id", "", "The ID of the organization that owns the bucket") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - deleteFlags.OrgID = h - } - - deleteCmd.PersistentFlags().StringVarP(&deleteFlags.Org, "org", "o", "", "The name of the organization that owns the bucket") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - deleteFlags.Org = h +func cmdDelete() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete points from an influxDB bucket", + Short: "Delete points from influxDB", + Long: `Delete points from influxDB, by specify start, end time + and a sql like predicate string.`, + RunE: wrapCheckSetup(fluxDeleteF), } - deleteCmd.PersistentFlags().StringVar(&deleteFlags.BucketID, "bucket-id", "", "The ID of destination bucket") - viper.BindEnv("BUCKET_ID") - if h := viper.GetString("BUCKET_ID"); h != "" { - deleteFlags.BucketID = h + opts := flagOpts{ + { + DestP: &deleteFlags.OrgID, + Flag: "org-id", + Desc: "The ID of the organization that owns the bucket", + Persistent: true, + }, + { + DestP: &deleteFlags.Org, + Flag: "org", + Short: 'o', + Desc: "The name of the organization that owns the bucket", + Persistent: true, + }, + { + DestP: &deleteFlags.BucketID, + Flag: "bucket-id", + Desc: "The ID of the destination bucket", + Persistent: true, + }, + { + DestP: &deleteFlags.Bucket, + Flag: "bucket", + Desc: "The name of destination bucket", + EnvVar: "BUCKET_NAME", + Persistent: true, + }, } + opts.mustRegister(cmd) - deleteCmd.PersistentFlags().StringVarP(&deleteFlags.Bucket, "bucket", "b", "", "The name of destination bucket") - viper.BindEnv("BUCKET_NAME") - if h := viper.GetString("BUCKET_NAME"); h != "" { - deleteFlags.Bucket = h - } + cmd.PersistentFlags().StringVar(&deleteFlags.Start, "start", "", "the start time in RFC3339Nano format, exp 2009-01-02T23:00:00Z") + cmd.PersistentFlags().StringVar(&deleteFlags.Stop, "stop", "", "the stop time in RFC3339Nano format, exp 2009-01-02T23:00:00Z") + cmd.PersistentFlags().StringVarP(&deleteFlags.Predicate, "predicate", "p", "", "sql like predicate string, exp 'tag1=\"v1\" and (tag2=123)'") - deleteCmd.PersistentFlags().StringVarP(&deleteFlags.Start, "start", "", "", "the start time in RFC3339Nano format, exp 2009-01-02T23:00:00Z") - deleteCmd.PersistentFlags().StringVarP(&deleteFlags.Stop, "stop", "", "", "the stop time in RFC3339Nano format, exp 2009-01-02T23:00:00Z") - deleteCmd.PersistentFlags().StringVarP(&deleteFlags.Predicate, "predicate", "p", "", "sql like predicate string, exp 'tag1=\"v1\" and (tag2=123)'") + return cmd } func fluxDeleteF(cmd *cobra.Command, args []string) error { diff --git a/cmd/influx/inspect.go b/cmd/influx/inspect.go deleted file mode 100644 index 3cc9bd986cf..00000000000 --- a/cmd/influx/inspect.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/internal/fs" - "github.com/influxdata/influxdb/tsdb/tsm1" - "github.com/spf13/cobra" -) - -// InspectReportTSMFlags defines the `report-tsm` Command. -type InspectReportTSMFlags struct { - pattern string - exact bool - detailed bool - organization - bucketID string - dataDir string -} - -var inspectReportTSMFlags InspectReportTSMFlags - -func initInspectReportTSMCommand() *cobra.Command { - inspectReportTSMCommand := &cobra.Command{ - Use: "report-tsm", - Short: "Run a TSM report", - Long: `This command will analyze TSM files within a storage engine -directory, reporting the cardinality within the files as well as the time range that -the point data covers. - -This command only interrogates the index within each file, and does not read any -block data. To reduce heap requirements, by default report-tsm estimates the overall -cardinality in the file set by using the HLL++ algorithm. Exact cardinalities can -be determined by using the --exact flag. - -For each file, the following is output: - - * The full filename; - * The series cardinality within the file; - * The number of series first encountered within the file; - * The minimum and maximum timestamp associated with any TSM data in the file; and - * The time taken to load the TSM index and apply any tombstones. - -The summary section then outputs the total time range and series cardinality for -the fileset. Depending on the --detailed flag, series cardinality is segmented -in the following ways: - - * Series cardinality for each organization; - * Series cardinality for each bucket; - * Series cardinality for each measurement; - * Number of field keys for each measurement; and - * Number of tag values for each tag key. -`, - RunE: inspectReportTSMF, - } - - inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.pattern, "pattern", "", "", "only process TSM files containing pattern") - inspectReportTSMCommand.Flags().BoolVarP(&inspectReportTSMFlags.exact, "exact", "", false, "calculate and exact cardinality count. Warning, may use significant memory...") - inspectReportTSMCommand.Flags().BoolVarP(&inspectReportTSMFlags.detailed, "detailed", "", false, "emit series cardinality segmented by measurements, tag keys and fields. Warning, may take a while.") - - inspectReportTSMFlags.organization.register(inspectReportTSMCommand) - inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.bucketID, "bucket-id", "", "", "process only data belonging to bucket ID. Requires org flag to be set.") - - dir, err := fs.InfluxDir() - if err != nil { - panic(err) - } - inspectReportTSMCommand.Flags().StringVarP(&inspectReportTSMFlags.dataDir, "data-dir", "", "", fmt.Sprintf("use provided data directory (defaults to %s).", filepath.Join(dir, "engine/data"))) - return inspectReportTSMCommand -} - -// inspectReportTSMF runs the report-tsm tool. -func inspectReportTSMF(cmd *cobra.Command, args []string) error { - if err := inspectReportTSMFlags.organization.validOrgFlags(); err != nil { - return err - } - report := &tsm1.Report{ - Stderr: os.Stderr, - Stdout: os.Stdout, - Dir: inspectReportTSMFlags.dataDir, - Pattern: inspectReportTSMFlags.pattern, - Detailed: inspectReportTSMFlags.detailed, - Exact: inspectReportTSMFlags.exact, - } - - if (inspectReportTSMFlags.organization.name == "" || inspectReportTSMFlags.organization.id == "") && inspectReportTSMFlags.bucketID != "" { - return errors.New("org-id must be set for non-empty bucket-id") - } - - orgSvc, err := newOrganizationService() - if err != nil { - return err - } - id, err := inspectReportTSMFlags.organization.getID(orgSvc) - if err != nil { - return err - } - report.OrgID = &id - - if inspectReportTSMFlags.bucketID != "" { - bucketID, err := influxdb.IDFromString(inspectReportTSMFlags.bucketID) - if err != nil { - return err - } - report.BucketID = bucketID - } - - _, err = report.Run(true) - if err != nil { - panic(err) - } - return err -} diff --git a/cmd/influx/main.go b/cmd/influx/main.go index 64f8cfe0d5c..d4fd6b0e4a1 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -7,12 +7,14 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/bolt" "github.com/influxdata/influxdb/cmd/influx/internal" "github.com/influxdata/influxdb/http" "github.com/influxdata/influxdb/internal/fs" + "github.com/influxdata/influxdb/kit/cli" "github.com/influxdata/influxdb/kv" "github.com/influxdata/influxdb/pkg/httpc" "github.com/spf13/cobra" @@ -115,39 +117,49 @@ func influxCmd() *cobra.Command { } viper.SetEnvPrefix("INFLUX") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) cmd.AddCommand( - authCmd(), - bucketCmd, - deleteCmd, - organizationCmd(), - pingCmd, + cmdAuth(), + cmdBucket(), + cmdDelete(), + cmdOrganization(), + cmdPing(), cmdPkg(newPkgerSVC), - queryCmd, - transpileCmd, - replCmd, - setupCmd, - taskCmd, - userCmd(), - writeCmd, + cmdQuery(), + cmdTranspile(), + cmdREPL(), + cmdSetup(), + cmdTask(), + cmdUser(), + cmdWrite(), ) - cmd.PersistentFlags().StringVarP(&flags.token, "token", "t", "", "API token to be used throughout client calls") - viper.BindEnv("TOKEN") - if h := viper.GetString("TOKEN"); h != "" { - flags.token = h - } else if tok, err := getTokenFromDefaultPath(); err == nil { - flags.token = tok + opts := flagOpts{ + { + DestP: &flags.token, + Flag: "token", + Short: 't', + Desc: "API token to be used throughout client calls", + Persistent: true, + }, + { + DestP: &flags.host, + Flag: "host", + Default: "http://localhost:9999", + Desc: "HTTP address of Influx", + Persistent: true, + }, } + opts.mustRegister(cmd) - cmd.PersistentFlags().StringVar(&flags.host, "host", "http://localhost:9999", "HTTP address of Influx") - viper.BindEnv("HOST") - if h := viper.GetString("HOST"); h != "" { - flags.host = h - } + // this is after the flagOpts register b/c we don't want to show the default value + // in the usage display. This will add it as the token value, then if a token flag + // is provided too, the flag will take precedence. + flags.token = getTokenFromDefaultPath() cmd.PersistentFlags().BoolVar(&flags.local, "local", false, "Run commands locally against the filesystem") - cmd.PersistentFlags().BoolVar(&flags.skipVerify, "skip-verify", false, "SkipVerify controls whether a client verifies the server's certificate chain and host name.") // Update help description for all commands in command tree @@ -158,16 +170,13 @@ func influxCmd() *cobra.Command { return cmd } -// Flags contains all the CLI flag values for influx. -type Flags struct { +var flags struct { token string host string local bool skipVerify bool } -var flags Flags - func defaultTokenPath() (string, string, error) { dir, err := fs.InfluxDir() if err != nil { @@ -176,16 +185,16 @@ func defaultTokenPath() (string, string, error) { return filepath.Join(dir, "credentials"), dir, nil } -func getTokenFromDefaultPath() (string, error) { +func getTokenFromDefaultPath() string { path, _, err := defaultTokenPath() if err != nil { - return "", err + return "" } b, err := ioutil.ReadFile(path) if err != nil { - return "", err + return "" } - return string(b), nil + return string(b) } func writeTokenToPath(tok, path, dir string) error { @@ -265,29 +274,35 @@ type organization struct { id, name string } -func (org *organization) register(cmd *cobra.Command) { - cmd.Flags().StringVarP(&org.id, "org-id", "", "", "The ID of the organization that owns the bucket") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - org.id = h - } - cmd.Flags().StringVarP(&org.name, "org", "o", "", "The name of the organization that owns the bucket") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - org.name = h +func (o *organization) register(cmd *cobra.Command, persistent bool) { + opts := flagOpts{ + { + DestP: &o.id, + Flag: "org-id", + Desc: "The ID of the organization that owns the bucket", + Persistent: persistent, + }, + { + DestP: &o.name, + Flag: "org", + Short: 'o', + Desc: "The name of the organization that owns the bucket", + Persistent: persistent, + }, } + opts.mustRegister(cmd) } -func (org *organization) getID(orgSVC influxdb.OrganizationService) (influxdb.ID, error) { - if org.id != "" { - influxOrgID, err := influxdb.IDFromString(org.id) +func (o *organization) getID(orgSVC influxdb.OrganizationService) (influxdb.ID, error) { + if o.id != "" { + influxOrgID, err := influxdb.IDFromString(o.id) if err != nil { return 0, fmt.Errorf("invalid org ID provided: %s", err.Error()) } return *influxOrgID, nil - } else if org.name != "" { + } else if o.name != "" { org, err := orgSVC.FindOrganization(context.Background(), influxdb.OrganizationFilter{ - Name: &org.name, + Name: &o.name, }) if err != nil { return 0, fmt.Errorf("%v", err) @@ -297,11 +312,29 @@ func (org *organization) getID(orgSVC influxdb.OrganizationService) (influxdb.ID return 0, fmt.Errorf("failed to locate an organization id") } -func (org *organization) validOrgFlags() error { - if org.id == "" && org.name == "" { +func (o *organization) validOrgFlags() error { + if o.id == "" && o.name == "" { return fmt.Errorf("must specify org-id, or org name") - } else if org.id != "" && org.name != "" { + } else if o.id != "" && o.name != "" { return fmt.Errorf("must specify org-id, or org name not both") } return nil } + +type flagOpts []cli.Opt + +func (f flagOpts) mustRegister(cmd *cobra.Command) { + for i := range f { + envVar := f[i].Flag + if e := f[i].EnvVar; e != "" { + envVar = e + } + + f[i].Desc = fmt.Sprintf( + "%s; Maps to env var $INFLUX_%s", + f[i].Desc, + strings.ToUpper(strings.Replace(envVar, "-", "_", -1)), + ) + } + cli.BindOptions(cmd, f) +} diff --git a/cmd/influx/organization.go b/cmd/influx/organization.go index fe4db74b040..4f4fb862f36 100644 --- a/cmd/influx/organization.go +++ b/cmd/influx/organization.go @@ -9,10 +9,9 @@ import ( "github.com/influxdata/influxdb/cmd/influx/internal" "github.com/influxdata/influxdb/http" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -func organizationCmd() *cobra.Command { +func cmdOrganization() *cobra.Command { cmd := &cobra.Command{ Use: "org", Aliases: []string{"organization"}, @@ -31,13 +30,10 @@ func organizationCmd() *cobra.Command { return cmd } -// Create Command -type OrganizationCreateFlags struct { +var organizationCreateFlags struct { name string } -var organizationCreateFlags OrganizationCreateFlags - func orgCreateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create", @@ -94,14 +90,11 @@ func organizationCreateF(cmd *cobra.Command, args []string) error { return nil } -// Find Command -type OrganizationFindFlags struct { +var organizationFindFlags struct { name string id string } -var organizationFindFlags OrganizationFindFlags - func orgFindCmd() *cobra.Command { cmd := &cobra.Command{ Use: "find", @@ -109,16 +102,23 @@ func orgFindCmd() *cobra.Command { RunE: wrapCheckSetup(organizationFindF), } - cmd.Flags().StringVarP(&organizationFindFlags.name, "name", "n", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - organizationFindFlags.name = h - } - cmd.Flags().StringVarP(&organizationFindFlags.id, "id", "i", "", "The organization ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationFindFlags.id = h - } + opts := flagOpts{ + { + DestP: &organizationFindFlags.name, + Flag: "name", + Short: 'n', + EnvVar: "ORG", + Desc: "The organization name", + }, + { + DestP: &organizationFindFlags.id, + Flag: "id", + Short: 'i', + EnvVar: "ORG_ID", + Desc: "The organization ID", + }, + } + opts.mustRegister(cmd) return cmd } @@ -163,14 +163,11 @@ func organizationFindF(cmd *cobra.Command, args []string) error { return nil } -// Update Command -type OrganizationUpdateFlags struct { +var organizationUpdateFlags struct { id string name string } -var organizationUpdateFlags OrganizationUpdateFlags - func orgUpdateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "update", @@ -178,18 +175,24 @@ func orgUpdateCmd() *cobra.Command { RunE: wrapCheckSetup(organizationUpdateF), } - cmd.Flags().StringVarP(&organizationUpdateFlags.id, "id", "i", "", "The organization ID (required)") - cmd.MarkFlagRequired("id") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationUpdateFlags.id = h - } - - cmd.Flags().StringVarP(&organizationUpdateFlags.name, "name", "n", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - organizationUpdateFlags.name = h - } + opts := flagOpts{ + { + DestP: &organizationUpdateFlags.id, + Flag: "id", + Short: 'i', + EnvVar: "ORG_ID", + Desc: "The organization ID (required)", + Required: true, + }, + { + DestP: &organizationUpdateFlags.name, + Flag: "name", + Short: 'n', + EnvVar: "ORG", + Desc: "The organization name", + }, + } + opts.mustRegister(cmd) return cmd } @@ -229,13 +232,10 @@ func organizationUpdateF(cmd *cobra.Command, args []string) error { return nil } -// OrganizationDeleteFlags contains the flag of the org delete command -type OrganizationDeleteFlags struct { +var organizationDeleteFlags struct { id string } -var organizationDeleteFlags OrganizationDeleteFlags - func organizationDeleteF(cmd *cobra.Command, args []string) error { orgSvc, err := newOrganizationService() if err != nil { @@ -280,16 +280,26 @@ func orgDeleteCmd() *cobra.Command { RunE: wrapCheckSetup(organizationDeleteF), } - cmd.Flags().StringVarP(&organizationDeleteFlags.id, "id", "i", "", "The organization ID (required)") - cmd.MarkFlagRequired("id") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationUpdateFlags.id = h + opts := flagOpts{ + { + DestP: &organizationFindFlags.id, + Flag: "id", + Short: 'i', + EnvVar: "ORG_ID", + Desc: "The organization ID", + }, } + opts.mustRegister(cmd) return cmd } +var orgMemberFlags struct { + name string + id string + memberID string +} + func orgMembersCmd() *cobra.Command { cmd := &cobra.Command{ Use: "members", @@ -297,6 +307,26 @@ func orgMembersCmd() *cobra.Command { Run: seeHelp, } + opts := flagOpts{ + { + DestP: &orgMemberFlags.name, + Flag: "name", + Short: 'n', + EnvVar: "ORG", + Desc: "The organization name", + Persistent: true, + }, + { + DestP: &orgMemberFlags.id, + Flag: "id", + Short: 'i', + EnvVar: "ORG_ID", + Desc: "The organization ID", + Persistent: true, + }, + } + opts.mustRegister(cmd) + cmd.AddCommand( orgMembersAddCmd(), orgMembersListCmd(), @@ -306,34 +336,26 @@ func orgMembersCmd() *cobra.Command { return cmd } -// List Members -type OrganizationMembersListFlags struct { - name string - id string -} - -var organizationMembersListFlags OrganizationMembersListFlags - func organizationMembersListF(cmd *cobra.Command, args []string) error { orgSvc, err := newOrganizationService() if err != nil { return fmt.Errorf("failed to initialize org service client: %v", err) } - if organizationMembersListFlags.id == "" && organizationMembersListFlags.name == "" { + if orgMemberFlags.id == "" && orgMemberFlags.name == "" { return fmt.Errorf("must specify exactly one of id and name") } filter := platform.OrganizationFilter{} - if organizationMembersListFlags.name != "" { - filter.Name = &organizationMembersListFlags.name + if orgMemberFlags.name != "" { + filter.Name = &orgMemberFlags.name } - if organizationMembersListFlags.id != "" { + if orgMemberFlags.id != "" { var fID platform.ID - err := fID.DecodeFromString(organizationMembersListFlags.id) + err := fID.DecodeFromString(orgMemberFlags.id) if err != nil { - return fmt.Errorf("failed to decode org id %s: %v", organizationMembersListFlags.id, err) + return fmt.Errorf("failed to decode org id %s: %v", orgMemberFlags.id, err) } filter.ID = &fID } @@ -351,41 +373,19 @@ func organizationMembersListF(cmd *cobra.Command, args []string) error { } func orgMembersListCmd() *cobra.Command { - cmd := &cobra.Command{ + return &cobra.Command{ Use: "list", Short: "List organization members", RunE: wrapCheckSetup(organizationMembersListF), } - - cmd.Flags().StringVarP(&organizationMembersListFlags.id, "id", "i", "", "The organization ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationMembersListFlags.id = h - } - cmd.Flags().StringVarP(&organizationMembersListFlags.name, "name", "n", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - organizationMembersListFlags.name = h - } - - return cmd } -// OrganizationMembersAddFlags includes flags to add a member -type OrganizationMembersAddFlags struct { - name string - id string - memberID string -} - -var organizationMembersAddFlags OrganizationMembersAddFlags - func organizationMembersAddF(cmd *cobra.Command, args []string) error { - if organizationMembersAddFlags.id == "" && organizationMembersAddFlags.name == "" { + if orgMemberFlags.id == "" && orgMemberFlags.name == "" { return fmt.Errorf("must specify exactly one of id and name") } - if organizationMembersAddFlags.id != "" && organizationMembersAddFlags.name != "" { + if orgMemberFlags.id != "" && orgMemberFlags.name != "" { return fmt.Errorf("must specify exactly one of id and name") } @@ -395,15 +395,15 @@ func organizationMembersAddF(cmd *cobra.Command, args []string) error { } filter := platform.OrganizationFilter{} - if organizationMembersAddFlags.name != "" { - filter.Name = &organizationMembersListFlags.name + if orgMemberFlags.name != "" { + filter.Name = &orgMemberFlags.name } - if organizationMembersAddFlags.id != "" { + if orgMemberFlags.id != "" { var fID platform.ID - err := fID.DecodeFromString(organizationMembersAddFlags.id) + err := fID.DecodeFromString(orgMemberFlags.id) if err != nil { - return fmt.Errorf("failed to decode org id %s: %v", organizationMembersAddFlags.id, err) + return fmt.Errorf("failed to decode org id %s: %v", orgMemberFlags.id, err) } filter.ID = &fID } @@ -415,9 +415,9 @@ func organizationMembersAddF(cmd *cobra.Command, args []string) error { } var memberID platform.ID - err = memberID.DecodeFromString(organizationMembersAddFlags.memberID) + err = memberID.DecodeFromString(orgMemberFlags.memberID) if err != nil { - return fmt.Errorf("failed to decode member id %s: %v", organizationMembersAddFlags.memberID, err) + return fmt.Errorf("failed to decode member id %s: %v", orgMemberFlags.memberID, err) } return membersAddF(ctx, platform.UserResourceMapping{ @@ -436,38 +436,18 @@ func orgMembersAddCmd() *cobra.Command { RunE: wrapCheckSetup(organizationMembersAddF), } - cmd.Flags().StringVarP(&organizationMembersAddFlags.id, "id", "i", "", "The organization ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationMembersAddFlags.id = h - } - cmd.Flags().StringVarP(&organizationMembersAddFlags.name, "name", "n", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - organizationMembersAddFlags.name = h - } - - cmd.Flags().StringVarP(&organizationMembersAddFlags.memberID, "member", "o", "", "The member ID") + cmd.Flags().StringVarP(&orgMemberFlags.memberID, "member", "o", "", "The member ID") cmd.MarkFlagRequired("member") return cmd } -// OrganizationMembersRemoveFlags includes flags to remove a Member -type OrganizationMembersRemoveFlags struct { - name string - id string - memberID string -} - -var organizationMembersRemoveFlags OrganizationMembersRemoveFlags - func organizationMembersRemoveF(cmd *cobra.Command, args []string) error { - if organizationMembersRemoveFlags.id == "" && organizationMembersRemoveFlags.name == "" { + if orgMemberFlags.id == "" && orgMemberFlags.name == "" { return fmt.Errorf("must specify exactly one of id and name") } - if organizationMembersRemoveFlags.id != "" && organizationMembersRemoveFlags.name != "" { + if orgMemberFlags.id != "" && orgMemberFlags.name != "" { return fmt.Errorf("must specify exactly one of id and name") } @@ -477,15 +457,15 @@ func organizationMembersRemoveF(cmd *cobra.Command, args []string) error { } filter := platform.OrganizationFilter{} - if organizationMembersRemoveFlags.name != "" { - filter.Name = &organizationMembersRemoveFlags.name + if orgMemberFlags.name != "" { + filter.Name = &orgMemberFlags.name } - if organizationMembersRemoveFlags.id != "" { + if orgMemberFlags.id != "" { var fID platform.ID - err := fID.DecodeFromString(organizationMembersRemoveFlags.id) + err := fID.DecodeFromString(orgMemberFlags.id) if err != nil { - return fmt.Errorf("failed to decode org id %s: %v", organizationMembersRemoveFlags.id, err) + return fmt.Errorf("failed to decode org id %s: %v", orgMemberFlags.id, err) } filter.ID = &fID } @@ -497,9 +477,9 @@ func organizationMembersRemoveF(cmd *cobra.Command, args []string) error { } var memberID platform.ID - err = memberID.DecodeFromString(organizationMembersRemoveFlags.memberID) + err = memberID.DecodeFromString(orgMemberFlags.memberID) if err != nil { - return fmt.Errorf("failed to decode member id %s: %v", organizationMembersRemoveFlags.memberID, err) + return fmt.Errorf("failed to decode member id %s: %v", orgMemberFlags.memberID, err) } return membersRemoveF(ctx, organization.ID, memberID) @@ -512,17 +492,7 @@ func orgMembersRemoveCmd() *cobra.Command { RunE: wrapCheckSetup(organizationMembersRemoveF), } - cmd.Flags().StringVarP(&organizationMembersRemoveFlags.id, "id", "i", "", "The organization ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - organizationMembersAddFlags.id = h - } - cmd.Flags().StringVarP(&organizationMembersRemoveFlags.name, "name", "n", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - organizationMembersRemoveFlags.name = h - } - cmd.Flags().StringVarP(&organizationMembersRemoveFlags.memberID, "member", "o", "", "The member ID") + cmd.Flags().StringVarP(&orgMemberFlags.memberID, "member", "o", "", "The member ID") cmd.MarkFlagRequired("member") return cmd diff --git a/cmd/influx/ping.go b/cmd/influx/ping.go index 6dbcbe31dbd..531e560c388 100644 --- a/cmd/influx/ping.go +++ b/cmd/influx/ping.go @@ -10,41 +10,43 @@ import ( "github.com/spf13/cobra" ) -var pingCmd = &cobra.Command{ - Use: "ping", - Short: "Check the InfluxDB /health endpoint", - Long: `Checks the health of a running InfluxDB instance by querying /health. Does not require valid token.`, - RunE: pingF, -} - -func pingF(cmd *cobra.Command, args []string) error { - if flags.local { - return fmt.Errorf("local flag not supported for ping command") - } - - c := http.Client{ - Timeout: 5 * time.Second, - } - url := flags.host + "/health" - resp, err := c.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode/100 != 2 { - return fmt.Errorf("got %d from '%s'", resp.StatusCode, url) - } - - var healthResponse check.Response - if err = json.NewDecoder(resp.Body).Decode(&healthResponse); err != nil { - return err - } - - if healthResponse.Status == check.StatusPass { - fmt.Println("OK") - } else { - return fmt.Errorf("health check failed: '%s'", healthResponse.Message) +func cmdPing() *cobra.Command { + cmd := &cobra.Command{ + Use: "ping", + Short: "Check the InfluxDB /health endpoint", + Long: `Checks the health of a running InfluxDB instance by querying /health. Does not require valid token.`, + RunE: func(cmd *cobra.Command, args []string) error { + if flags.local { + return fmt.Errorf("local flag not supported for ping command") + } + + c := http.Client{ + Timeout: 5 * time.Second, + } + url := flags.host + "/health" + resp, err := c.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode/100 != 2 { + return fmt.Errorf("got %d from '%s'", resp.StatusCode, url) + } + + var healthResponse check.Response + if err = json.NewDecoder(resp.Body).Decode(&healthResponse); err != nil { + return err + } + + if healthResponse.Status == check.StatusPass { + fmt.Println("OK") + } else { + return fmt.Errorf("health check failed: '%s'", healthResponse.Message) + } + + return nil + }, } - return nil + return cmd } diff --git a/cmd/influx/pkg.go b/cmd/influx/pkg.go index c9c0f3605b1..3bf1a2cf579 100644 --- a/cmd/influx/pkg.go +++ b/cmd/influx/pkg.go @@ -98,7 +98,7 @@ func (b *cmdPkgBuilder) cmdPkgApply() *cobra.Command { 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"`) - b.org.register(cmd) + b.org.register(cmd, false) cmd.Flags().BoolVarP(&b.hasColor, "color", "c", true, "Enable color in output, defaults true") cmd.Flags().BoolVar(&b.hasTableBorders, "table-borders", true, "Enable table borders, defaults true") @@ -338,7 +338,7 @@ func (b *cmdPkgBuilder) cmdPkgExportAll() *cobra.Command { cmd.Flags().StringVarP(&b.file, "file", "f", "", "output file for created pkg; defaults to std out if no file provided; the extension of provided file (.yml/.json) will dictate encoding") - b.org.register(cmd) + b.org.register(cmd, false) cmd.Flags().StringVarP(&b.meta.Name, "name", "n", "", "name for new pkg") cmd.Flags().StringVarP(&b.meta.Description, "description", "d", "", "description for new pkg") diff --git a/cmd/influx/query.go b/cmd/influx/query.go index dc2bae9124a..b179faec88c 100644 --- a/cmd/influx/query.go +++ b/cmd/influx/query.go @@ -10,35 +10,41 @@ import ( platform "github.com/influxdata/influxdb" _ "github.com/influxdata/influxdb/query/stdlib" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var queryCmd = &cobra.Command{ - Use: "query [query literal or @/path/to/query.flux]", - Short: "Execute a Flux query", - Long: `Execute a literal Flux query provided as a string, -or execute a literal Flux query contained in a file by specifying the file prefixed with an @ sign.`, - Args: cobra.ExactArgs(1), - RunE: wrapCheckSetup(fluxQueryF), -} - var queryFlags struct { OrgID string Org string } -func init() { - queryCmd.PersistentFlags().StringVar(&queryFlags.OrgID, "org-id", "", "The organization ID") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - queryFlags.OrgID = h +func cmdQuery() *cobra.Command { + cmd := &cobra.Command{ + Use: "query [query literal or @/path/to/query.flux]", + Short: "Execute a Flux query", + Long: `Execute a literal Flux query provided as a string, +or execute a literal Flux query contained in a file by specifying the file prefixed with an @ sign.`, + Args: cobra.ExactArgs(1), + RunE: wrapCheckSetup(fluxQueryF), } - queryCmd.PersistentFlags().StringVarP(&queryFlags.Org, "org", "o", "", "The organization name") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - queryFlags.Org = h + opts := flagOpts{ + { + DestP: &queryFlags.OrgID, + Flag: "org-id", + Desc: "The organization ID", + Persistent: true, + }, + { + DestP: &queryFlags.Org, + Flag: "org", + Short: 'o', + Desc: "The organization name", + Persistent: true, + }, } + opts.mustRegister(cmd) + + return cmd } func fluxQueryF(cmd *cobra.Command, args []string) error { diff --git a/cmd/influx/repl.go b/cmd/influx/repl.go index 58f9e3fa893..274a810bab3 100644 --- a/cmd/influx/repl.go +++ b/cmd/influx/repl.go @@ -12,33 +12,22 @@ import ( "github.com/influxdata/influxdb/query" _ "github.com/influxdata/influxdb/query/stdlib" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var replCmd = &cobra.Command{ - Use: "repl", - Short: "Interactive Flux REPL (read-eval-print-loop)", - Args: cobra.NoArgs, - RunE: wrapCheckSetup(replF), -} - var replFlags struct { - OrgID string - Org string + organization } -func init() { - replCmd.PersistentFlags().StringVar(&replFlags.OrgID, "org-id", "", "The ID of organization to query") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - replFlags.OrgID = h +func cmdREPL() *cobra.Command { + cmd := &cobra.Command{ + Use: "repl", + Short: "Interactive Flux REPL (read-eval-print-loop)", + Args: cobra.NoArgs, + RunE: wrapCheckSetup(replF), } + replFlags.organization.register(cmd, false) - replCmd.PersistentFlags().StringVarP(&replFlags.Org, "org", "o", "", "The name of the organization") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - replFlags.Org = h - } + return cmd } func replF(cmd *cobra.Command, args []string) error { @@ -46,29 +35,18 @@ func replF(cmd *cobra.Command, args []string) error { return fmt.Errorf("local flag not supported for repl command") } - if replFlags.OrgID == "" && replFlags.Org == "" { - return fmt.Errorf("must specify exactly one of org or org-id") - } - - if replFlags.OrgID != "" && replFlags.Org != "" { - return fmt.Errorf("must specify exactly one of org or org-id") + if err := replFlags.organization.validOrgFlags(); err != nil { + return err } - var orgID platform.ID - if replFlags.OrgID != "" { - err := orgID.DecodeFromString(replFlags.OrgID) - if err != nil { - return fmt.Errorf("invalid org id: %v", err) - } + orgSVC, err := newOrganizationService() + if err != nil { + return err } - if replFlags.Org != "" { - ctx := context.Background() - var err error - orgID, err = findOrgID(ctx, replFlags.Org) - if err != nil { - return fmt.Errorf("unable to find organization: %v", err) - } + orgID, err := replFlags.organization.getID(orgSVC) + if err != nil { + return err } flux.FinalizeBuiltIns() @@ -82,25 +60,6 @@ func replF(cmd *cobra.Command, args []string) error { return nil } -func findOrgID(ctx context.Context, org string) (platform.ID, error) { - client, err := newHTTPClient() - if err != nil { - return 0, err - } - svc := &http.OrganizationService{ - Client: client, - } - - o, err := svc.FindOrganization(ctx, platform.OrganizationFilter{ - Name: &org, - }) - if err != nil { - return platform.InvalidID(), err - } - - return o.ID, nil -} - func getFluxREPL(addr, token string, skipVerify bool, orgID platform.ID) (*repl.REPL, error) { qs := &http.FluxQueryService{ Addr: addr, @@ -111,7 +70,7 @@ func getFluxREPL(addr, token string, skipVerify bool, orgID platform.ID) (*repl. OrganizationID: orgID, QueryService: qs, } - // background context is OK here, and DefaultDependencies are noop deps. Also safe since we send all queries to the - // server side. + // background context is OK here, and DefaultDependencies are noop deps. Also safe + // since we send all queries to the server side. return repl.New(context.Background(), flux.NewDefaultDependencies(), q), nil } diff --git a/cmd/influx/setup.go b/cmd/influx/setup.go index e3016df2044..61ae5af35e3 100644 --- a/cmd/influx/setup.go +++ b/cmd/influx/setup.go @@ -14,15 +14,7 @@ import ( input "github.com/tcnksm/go-input" ) -// setup Command -var setupCmd = &cobra.Command{ - Use: "setup", - Short: "Setup instance with initial user, org, bucket", - RunE: wrapErrorFmt(setupF), -} - -// SetupFlags are used when setup is not in interactive mode. -type SetupFlags struct { +var setupFlags struct { username string password string token string @@ -32,16 +24,22 @@ type SetupFlags struct { force bool } -var setupFlags SetupFlags +func cmdSetup() *cobra.Command { + cmd := &cobra.Command{ + Use: "setup", + Short: "Setup instance with initial user, org, bucket", + RunE: wrapErrorFmt(setupF), + } + + cmd.Flags().StringVarP(&setupFlags.username, "username", "u", "", "primary username") + cmd.Flags().StringVarP(&setupFlags.password, "password", "p", "", "password for username") + cmd.Flags().StringVarP(&setupFlags.token, "token", "t", "", "token for username, else auto-generated") + cmd.Flags().StringVarP(&setupFlags.org, "org", "o", "", "primary organization name") + cmd.Flags().StringVarP(&setupFlags.bucket, "bucket", "b", "", "primary bucket name") + cmd.Flags().IntVarP(&setupFlags.retention, "retention", "r", -1, "retention period in hours, else infinite") + cmd.Flags().BoolVarP(&setupFlags.force, "force", "f", false, "skip confirmation prompt") -func init() { - setupCmd.Flags().StringVarP(&setupFlags.username, "username", "u", "", "primary username") - setupCmd.Flags().StringVarP(&setupFlags.password, "password", "p", "", "password for username") - setupCmd.Flags().StringVarP(&setupFlags.token, "token", "t", "", "token for username, else auto-generated") - setupCmd.Flags().StringVarP(&setupFlags.org, "org", "o", "", "primary organization name") - setupCmd.Flags().StringVarP(&setupFlags.bucket, "bucket", "b", "", "primary bucket name") - setupCmd.Flags().IntVarP(&setupFlags.retention, "retention", "r", -1, "retention period in hours, else infinite") - setupCmd.Flags().BoolVarP(&setupFlags.force, "force", "f", false, "skip confirmation prompt") + return cmd } func setupF(cmd *cobra.Command, args []string) error { diff --git a/cmd/influx/task.go b/cmd/influx/task.go index 16f53d8cff9..bfffa8602c6 100644 --- a/cmd/influx/task.go +++ b/cmd/influx/task.go @@ -13,55 +13,47 @@ import ( "github.com/spf13/cobra" ) -// task Command -var taskCmd = &cobra.Command{ - Use: "task", - Short: "Task management commands", - RunE: wrapCheckSetup(taskF), -} - -func taskF(cmd *cobra.Command, args []string) error { - if flags.local { - return fmt.Errorf("local flag not supported for task command") +func cmdTask() *cobra.Command { + cmd := &cobra.Command{ + Use: "task", + Short: "Task management commands", + RunE: wrapCheckSetup(func(cmd *cobra.Command, args []string) error { + if flags.local { + return fmt.Errorf("local flag not supported for task command") + } + + seeHelp(cmd, args) + return nil + }), } - seeHelp(cmd, args) - return nil -} - -var logCmd = &cobra.Command{ - Use: "log", - Short: "Log related commands", - Run: seeHelp, -} - -var runCmd = &cobra.Command{ - Use: "run", - Short: "Run related commands", - Run: seeHelp, -} + cmd.AddCommand( + taskLogCmd(), + taskRunCmd(), + taskCreateCmd(), + taskDeleteCmd(), + taskFindCmd(), + taskUpdateCmd(), + ) -func init() { - taskCmd.AddCommand(runCmd) - taskCmd.AddCommand(logCmd) + return cmd } var taskCreateFlags struct { organization } -func init() { - taskCreateCmd := &cobra.Command{ +func taskCreateCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "create [query literal or @/path/to/query.flux]", Short: "Create task", Args: cobra.ExactArgs(1), RunE: wrapCheckSetup(taskCreateF), } - taskCreateFlags.organization.register(taskCreateCmd) - taskCreateCmd.MarkFlagRequired("flux") + taskCreateFlags.organization.register(cmd, false) - taskCmd.AddCommand(taskCreateCmd) + return cmd } func taskCreateF(cmd *cobra.Command, args []string) error { @@ -134,20 +126,20 @@ var taskFindFlags struct { organization } -func init() { - taskFindCmd := &cobra.Command{ +func taskFindCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "find", Short: "Find tasks", RunE: wrapCheckSetup(taskFindF), } - taskFindCmd.Flags().StringVarP(&taskFindFlags.id, "id", "i", "", "task ID") - taskFindCmd.Flags().StringVarP(&taskFindFlags.user, "user-id", "n", "", "task owner ID") - taskFindFlags.organization.register(taskFindCmd) - taskFindCmd.Flags().IntVarP(&taskFindFlags.limit, "limit", "", platform.TaskDefaultPageSize, "the number of tasks to find") - taskFindCmd.Flags().BoolVar(&taskFindFlags.headers, "headers", true, "To print the table headers; defaults true") + taskFindFlags.organization.register(cmd, false) + cmd.Flags().StringVarP(&taskFindFlags.id, "id", "i", "", "task ID") + cmd.Flags().StringVarP(&taskFindFlags.user, "user-id", "n", "", "task owner ID") + cmd.Flags().IntVarP(&taskFindFlags.limit, "limit", "", platform.TaskDefaultPageSize, "the number of tasks to find") + cmd.Flags().BoolVar(&taskFindFlags.headers, "headers", true, "To print the table headers; defaults true") - taskCmd.AddCommand(taskFindCmd) + return cmd } func taskFindF(cmd *cobra.Command, args []string) error { @@ -240,7 +232,7 @@ var taskUpdateFlags struct { status string } -func init() { +func taskUpdateCmd() *cobra.Command { taskUpdateCmd := &cobra.Command{ Use: "update", Short: "Update task", @@ -251,7 +243,7 @@ func init() { taskUpdateCmd.Flags().StringVarP(&taskUpdateFlags.status, "status", "", "", "update task status") taskUpdateCmd.MarkFlagRequired("id") - taskCmd.AddCommand(taskUpdateCmd) + return taskUpdateCmd } func taskUpdateF(cmd *cobra.Command, args []string) error { @@ -313,17 +305,17 @@ var taskDeleteFlags struct { id string } -func init() { - taskDeleteCmd := &cobra.Command{ +func taskDeleteCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "delete", Short: "Delete task", RunE: wrapCheckSetup(taskDeleteF), } - taskDeleteCmd.Flags().StringVarP(&taskDeleteFlags.id, "id", "i", "", "task id (required)") - taskDeleteCmd.MarkFlagRequired("id") + cmd.Flags().StringVarP(&taskDeleteFlags.id, "id", "i", "", "task id (required)") + cmd.MarkFlagRequired("id") - taskCmd.AddCommand(taskDeleteCmd) + return cmd } func taskDeleteF(cmd *cobra.Command, args []string) error { @@ -374,23 +366,37 @@ func taskDeleteF(cmd *cobra.Command, args []string) error { return nil } +func taskLogCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "log", + Short: "Log related commands", + Run: seeHelp, + } + + cmd.AddCommand( + taskLogFindCmd(), + ) + + return cmd +} + var taskLogFindFlags struct { taskID string runID string } -func init() { - taskLogFindCmd := &cobra.Command{ +func taskLogFindCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "find", Short: "find logs for task", RunE: wrapCheckSetup(taskLogFindF), } - taskLogFindCmd.Flags().StringVarP(&taskLogFindFlags.taskID, "task-id", "", "", "task id (required)") - taskLogFindCmd.Flags().StringVarP(&taskLogFindFlags.runID, "run-id", "", "", "run id") - taskLogFindCmd.MarkFlagRequired("task-id") + cmd.Flags().StringVarP(&taskLogFindFlags.taskID, "task-id", "", "", "task id (required)") + cmd.Flags().StringVarP(&taskLogFindFlags.runID, "run-id", "", "", "run id") + cmd.MarkFlagRequired("task-id") - logCmd.AddCommand(taskLogFindCmd) + return cmd } func taskLogFindF(cmd *cobra.Command, args []string) error { @@ -439,6 +445,20 @@ func taskLogFindF(cmd *cobra.Command, args []string) error { return nil } +func taskRunCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "run", + Short: "Run related commands", + Run: seeHelp, + } + cmd.AddCommand( + taskRunFindCmd(), + taskRunRetryCmd(), + ) + + return cmd +} + var taskRunFindFlags struct { runID string taskID string @@ -447,22 +467,22 @@ var taskRunFindFlags struct { limit int } -func init() { - taskRunFindCmd := &cobra.Command{ +func taskRunFindCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "find", Short: "find runs for a task", RunE: wrapCheckSetup(taskRunFindF), } - taskRunFindCmd.Flags().StringVarP(&taskRunFindFlags.taskID, "task-id", "", "", "task id (required)") - taskRunFindCmd.Flags().StringVarP(&taskRunFindFlags.runID, "run-id", "", "", "run id") - taskRunFindCmd.Flags().StringVarP(&taskRunFindFlags.afterTime, "after", "", "", "after time for filtering") - taskRunFindCmd.Flags().StringVarP(&taskRunFindFlags.beforeTime, "before", "", "", "before time for filtering") - taskRunFindCmd.Flags().IntVarP(&taskRunFindFlags.limit, "limit", "", 0, "limit the results") + cmd.Flags().StringVarP(&taskRunFindFlags.taskID, "task-id", "", "", "task id (required)") + cmd.Flags().StringVarP(&taskRunFindFlags.runID, "run-id", "", "", "run id") + cmd.Flags().StringVarP(&taskRunFindFlags.afterTime, "after", "", "", "after time for filtering") + cmd.Flags().StringVarP(&taskRunFindFlags.beforeTime, "before", "", "", "before time for filtering") + cmd.Flags().IntVarP(&taskRunFindFlags.limit, "limit", "", 0, "limit the results") - taskRunFindCmd.MarkFlagRequired("task-id") + cmd.MarkFlagRequired("task-id") - runCmd.AddCommand(taskRunFindCmd) + return cmd } func taskRunFindF(cmd *cobra.Command, args []string) error { @@ -537,7 +557,7 @@ var runRetryFlags struct { taskID, runID string } -func init() { +func taskRunRetryCmd() *cobra.Command { cmd := &cobra.Command{ Use: "retry", Short: "retry a run", @@ -549,7 +569,7 @@ func init() { cmd.MarkFlagRequired("task-id") cmd.MarkFlagRequired("run-id") - runCmd.AddCommand(cmd) + return cmd } func runRetryF(cmd *cobra.Command, args []string) error { diff --git a/cmd/influx/transpile.go b/cmd/influx/transpile.go index 610e5932fed..400d4ae10c8 100644 --- a/cmd/influx/transpile.go +++ b/cmd/influx/transpile.go @@ -10,31 +10,34 @@ import ( "github.com/influxdata/influxdb/kit/errors" "github.com/influxdata/influxdb/query/influxql" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var transpileCmd = &cobra.Command{ - Use: "transpile [InfluxQL query]", - Short: "Transpile an InfluxQL query to Flux source code", - Long: `Transpile an InfluxQL query to Flux source code. - -The transpiled query assumes that the bucket name is the of the form '/'. - -The transpiled query will be written for absolute time ranges using the provided now() time.`, - Args: cobra.ExactArgs(1), - RunE: transpileF, -} - var transpileFlags struct { Now string } -func init() { - transpileCmd.PersistentFlags().StringVar(&transpileFlags.Now, "now", "", "An RFC3339Nano formatted time to use as the now() time. Defaults to the current time.") - viper.BindEnv("NOW") - if h := viper.GetString("NOW"); h != "" { - transpileFlags.Now = h +func cmdTranspile() *cobra.Command { + cmd := &cobra.Command{ + Use: "transpile [InfluxQL query]", + Short: "Transpile an InfluxQL query to Flux source code", + Long: `Transpile an InfluxQL query to Flux source code. + +The transpiled query assumes that the bucket name is the of the form '/'. + +The transpiled query will be written for absolute time ranges using the provided now() time.`, + Args: cobra.ExactArgs(1), + RunE: transpileF, } + opts := flagOpts{ + { + DestP: &transpileFlags.Now, + Flag: "now", + Desc: "An RFC3339Nano formatted time to use as the now() time. Defaults to the current time", + }, + } + opts.mustRegister(cmd) + + return cmd } func transpileF(cmd *cobra.Command, args []string) error { @@ -58,8 +61,7 @@ func transpileF(cmd *cobra.Command, args []string) error { return nil } -type dbrpMapper struct { -} +type dbrpMapper struct{} func (m dbrpMapper) FindBy(ctx context.Context, cluster string, db string, rp string) (*influxdb.DBRPMapping, error) { return nil, errors.New("mapping not found") diff --git a/cmd/influx/user.go b/cmd/influx/user.go index 343cb44dc27..29307bbbf7d 100644 --- a/cmd/influx/user.go +++ b/cmd/influx/user.go @@ -11,7 +11,7 @@ import ( "github.com/spf13/cobra" ) -func userCmd() *cobra.Command { +func cmdUser() *cobra.Command { cmd := &cobra.Command{ Use: "user", Short: "User management commands", @@ -123,10 +123,10 @@ func userCreateCmd() *cobra.Command { RunE: wrapCheckSetup(userCreateF), } + userCreateFlags.organization.register(cmd, false) cmd.Flags().StringVarP(&userCreateFlags.name, "name", "n", "", "The user name (required)") cmd.MarkFlagRequired("name") cmd.Flags().StringVarP(&userCreateFlags.password, "password", "p", "", "The user password") - userCreateFlags.organization.register(cmd) return cmd } diff --git a/cmd/influx/write.go b/cmd/influx/write.go index 5bd013a9af6..14dd2077dd3 100644 --- a/cmd/influx/write.go +++ b/cmd/influx/write.go @@ -13,18 +13,8 @@ import ( "github.com/influxdata/influxdb/models" "github.com/influxdata/influxdb/write" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var writeCmd = &cobra.Command{ - Use: "write line protocol or @/path/to/points.txt", - Short: "Write points to InfluxDB", - Long: `Write a single line of line protocol to InfluxDB, -or add an entire file specified with an @ prefix.`, - Args: cobra.ExactArgs(1), - RunE: wrapCheckSetup(fluxWriteF), -} - var writeFlags struct { OrgID string Org string @@ -33,36 +23,56 @@ var writeFlags struct { Precision string } -func init() { - writeCmd.PersistentFlags().StringVar(&writeFlags.OrgID, "org-id", "", "The ID of the organization that owns the bucket") - viper.BindEnv("ORG_ID") - if h := viper.GetString("ORG_ID"); h != "" { - writeFlags.OrgID = h - } - - writeCmd.PersistentFlags().StringVarP(&writeFlags.Org, "org", "o", "", "The name of the organization that owns the bucket") - viper.BindEnv("ORG") - if h := viper.GetString("ORG"); h != "" { - writeFlags.Org = h - } - - writeCmd.PersistentFlags().StringVar(&writeFlags.BucketID, "bucket-id", "", "The ID of destination bucket") - viper.BindEnv("BUCKET_ID") - if h := viper.GetString("BUCKET_ID"); h != "" { - writeFlags.BucketID = h +func cmdWrite() *cobra.Command { + cmd := &cobra.Command{ + Use: "write line protocol or @/path/to/points.txt", + Short: "Write points to InfluxDB", + Long: `Write a single line of line protocol to InfluxDB, +or add an entire file specified with an @ prefix.`, + Args: cobra.ExactArgs(1), + RunE: wrapCheckSetup(fluxWriteF), } - writeCmd.PersistentFlags().StringVarP(&writeFlags.Bucket, "bucket", "b", "", "The name of destination bucket") - viper.BindEnv("BUCKET_NAME") - if h := viper.GetString("BUCKET_NAME"); h != "" { - writeFlags.Bucket = h + opts := flagOpts{ + { + DestP: &writeFlags.OrgID, + Flag: "org-id", + Desc: "The ID of the organization that owns the bucket", + Persistent: true, + }, + { + DestP: &writeFlags.Org, + Flag: "org", + Short: 'o', + Desc: "The name of the organization that owns the bucket", + Persistent: true, + }, + { + DestP: &writeFlags.BucketID, + Flag: "bucket-id", + Desc: "The ID of destination bucket", + Persistent: true, + }, + { + DestP: &writeFlags.Bucket, + Flag: "bucket", + Short: 'b', + EnvVar: "BUCKET_NAME", + Desc: "The name of destination bucket", + Persistent: true, + }, + { + DestP: &writeFlags.Precision, + Flag: "precision", + Short: 'p', + Default: "ns", + Desc: "Precision of the timestamps of the lines", + Persistent: true, + }, } + opts.mustRegister(cmd) - writeCmd.PersistentFlags().StringVarP(&writeFlags.Precision, "precision", "p", "ns", "Precision of the timestamps of the lines") - viper.BindEnv("PRECISION") - if p := viper.GetString("PRECISION"); p != "" { - writeFlags.Precision = p - } + return cmd } func fluxWriteF(cmd *cobra.Command, args []string) error { @@ -80,15 +90,11 @@ func fluxWriteF(cmd *cobra.Command, args []string) error { return fmt.Errorf("invalid precision") } - httpClient, err := newHTTPClient() + bs, err := newBucketService() if err != nil { return err } - bs := &http.BucketService{ - Client: httpClient, - } - var filter platform.BucketFilter if writeFlags.BucketID != "" { filter.ID, err = platform.IDFromString(writeFlags.BucketID) diff --git a/kit/cli/viper.go b/kit/cli/viper.go index 00c39c144ea..92110476bf5 100644 --- a/kit/cli/viper.go +++ b/kit/cli/viper.go @@ -6,13 +6,20 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/spf13/viper" ) // Opt is a single command-line option type Opt struct { - DestP interface{} // pointer to the destination - Flag string + DestP interface{} // pointer to the destination + + EnvVar string + Flag string + Persistent bool + Required bool + Short rune // using rune b/c it guarantees correctness. a short must always be a string of length 1 + Default interface{} Desc string } @@ -66,47 +73,83 @@ func NewCommand(p *Program) *cobra.Command { // registers those options with viper. func BindOptions(cmd *cobra.Command, opts []Opt) { for _, o := range opts { + flagset := cmd.Flags() + if o.Persistent { + flagset = cmd.PersistentFlags() + } + + if o.Required { + cmd.MarkFlagRequired(o.Flag) + } + + envVar := o.Flag + if o.EnvVar != "" { + envVar = o.EnvVar + } + + hasShort := o.Short != 0 + switch destP := o.DestP.(type) { case *string: var d string if o.Default != nil { d = o.Default.(string) } - cmd.Flags().StringVar(destP, o.Flag, d, o.Desc) - mustBindPFlag(o.Flag, cmd) - *destP = viper.GetString(o.Flag) + if hasShort { + flagset.StringVarP(destP, o.Flag, string(o.Short), d, o.Desc) + } else { + flagset.StringVar(destP, o.Flag, d, o.Desc) + } + mustBindPFlag(o.Flag, flagset) + *destP = viper.GetString(envVar) case *int: var d int if o.Default != nil { d = o.Default.(int) } - cmd.Flags().IntVar(destP, o.Flag, d, o.Desc) - mustBindPFlag(o.Flag, cmd) - *destP = viper.GetInt(o.Flag) + if hasShort { + flagset.IntVarP(destP, o.Flag, string(o.Short), d, o.Desc) + } else { + flagset.IntVar(destP, o.Flag, d, o.Desc) + } + mustBindPFlag(o.Flag, flagset) + *destP = viper.GetInt(envVar) case *bool: var d bool if o.Default != nil { d = o.Default.(bool) } - cmd.Flags().BoolVar(destP, o.Flag, d, o.Desc) - mustBindPFlag(o.Flag, cmd) - *destP = viper.GetBool(o.Flag) + if hasShort { + flagset.BoolVarP(destP, o.Flag, string(o.Short), d, o.Desc) + } else { + flagset.BoolVar(destP, o.Flag, d, o.Desc) + } + mustBindPFlag(o.Flag, flagset) + *destP = viper.GetBool(envVar) case *time.Duration: var d time.Duration if o.Default != nil { d = o.Default.(time.Duration) } - cmd.Flags().DurationVar(destP, o.Flag, d, o.Desc) - mustBindPFlag(o.Flag, cmd) - *destP = viper.GetDuration(o.Flag) + if hasShort { + flagset.DurationVarP(destP, o.Flag, string(o.Short), d, o.Desc) + } else { + flagset.DurationVar(destP, o.Flag, d, o.Desc) + } + mustBindPFlag(o.Flag, flagset) + *destP = viper.GetDuration(envVar) case *[]string: var d []string if o.Default != nil { d = o.Default.([]string) } - cmd.Flags().StringSliceVar(destP, o.Flag, d, o.Desc) - mustBindPFlag(o.Flag, cmd) - *destP = viper.GetStringSlice(o.Flag) + if hasShort { + flagset.StringSliceVarP(destP, o.Flag, string(o.Short), d, o.Desc) + } else { + flagset.StringSliceVar(destP, o.Flag, d, o.Desc) + } + mustBindPFlag(o.Flag, flagset) + *destP = viper.GetStringSlice(envVar) default: // if you get a panic here, sorry about that! // anyway, go ahead and make a PR and add another type. @@ -115,8 +158,8 @@ func BindOptions(cmd *cobra.Command, opts []Opt) { } } -func mustBindPFlag(key string, cmd *cobra.Command) { - if err := viper.BindPFlag(key, cmd.Flags().Lookup(key)); err != nil { +func mustBindPFlag(key string, flagset *pflag.FlagSet) { + if err := viper.BindPFlag(key, flagset.Lookup(key)); err != nil { panic(err) } }