From fb2c21719bea072845f853b97ba4242bc8e9270a Mon Sep 17 00:00:00 2001 From: Johnny Steenbergen Date: Mon, 22 Jun 2020 19:22:03 -0700 Subject: [PATCH] fix(influx): support days as duration type closes: #18639 --- cmd/influx/bucket.go | 23 ++++++++++++++------- cmd/influx/main.go | 44 +++++++++++++++++++++++++++++++++++++++++ cmd/influx/setup.go | 38 +++++++++++++++++++++-------------- task/options/options.go | 2 +- task/options/strconv.go | 3 +-- 5 files changed, 85 insertions(+), 25 deletions(-) diff --git a/cmd/influx/bucket.go b/cmd/influx/bucket.go index 10a6f37e886..bd7d50e864e 100644 --- a/cmd/influx/bucket.go +++ b/cmd/influx/bucket.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "time" "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/http" @@ -30,7 +29,7 @@ type cmdBucketBuilder struct { name string description string org organization - retention time.Duration + retention string } func newCmdBucketBuilder(svcsFn bucketSVCsFn, opts genericCLIOpts) *cmdBucketBuilder { @@ -72,7 +71,7 @@ func (b *cmdBucketBuilder) cmdCreate() *cobra.Command { opts.mustRegister(cmd) cmd.Flags().StringVarP(&b.description, "description", "d", "", "Description of bucket that will be created") - cmd.Flags().DurationVarP(&b.retention, "retention", "r", 0, "Duration bucket will retain data. 0 is infinite. Default is 0.") + cmd.Flags().StringVarP(&b.retention, "retention", "r", "0sec", "Duration bucket will retain data. 0 is infinite. Default is 0.") b.org.register(cmd, false) b.registerPrintFlags(cmd) @@ -89,10 +88,15 @@ func (b *cmdBucketBuilder) cmdCreateRunEFn(*cobra.Command, []string) error { return err } + dur, err := rawDurationToTimeDuration(b.retention) + if err != nil { + return err + } + bkt := &influxdb.Bucket{ Name: b.name, Description: b.description, - RetentionPeriod: b.retention, + RetentionPeriod: dur, } bkt.OrgID, err = b.org.getID(orgSVC) if err != nil { @@ -245,7 +249,7 @@ func (b *cmdBucketBuilder) cmdUpdate() *cobra.Command { cmd.Flags().StringVarP(&b.id, "id", "i", "", "The bucket ID (required)") cmd.Flags().StringVarP(&b.description, "description", "d", "", "Description of bucket that will be created") cmd.MarkFlagRequired("id") - cmd.Flags().DurationVarP(&b.retention, "retention", "r", 0, "Duration bucket will retain data. 0 is infinite. Default is 0.") + cmd.Flags().StringVarP(&b.retention, "retention", "r", "0s", "Duration bucket will retain data. 0 is infinite. Default is 0.") return cmd } @@ -268,8 +272,13 @@ func (b *cmdBucketBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) er if b.description != "" { update.Description = &b.description } - if b.retention != 0 { - update.RetentionPeriod = &b.retention + + dur, err := rawDurationToTimeDuration(b.retention) + if err != nil { + return err + } + if dur != 0 { + update.RetentionPeriod = &dur } bkt, err := bktSVC.UpdateBucket(context.Background(), id, update) diff --git a/cmd/influx/main.go b/cmd/influx/main.go index ac447a3fa68..a92eed305e9 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -20,6 +21,7 @@ import ( "github.com/influxdata/influxdb/v2/internal/fs" "github.com/influxdata/influxdb/v2/kit/cli" "github.com/influxdata/influxdb/v2/pkg/httpc" + "github.com/influxdata/influxdb/v2/task/options" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -535,3 +537,45 @@ func newBucketService() (influxdb.BucketService, error) { Client: client, }, nil } + +func rawDurationToTimeDuration(raw string) (time.Duration, error) { + if raw == "" { + return 0, nil + } + + if dur, err := time.ParseDuration(raw); err == nil { + return dur, nil + } + + retention, err := options.ParseSignedDuration(raw) + if err != nil { + return 0, err + } + + const day = 24 * time.Hour + + var dur time.Duration + for _, d := range retention.Values { + if d.Magnitude < 0 { + return 0, errors.New("must be greater than 0") + } + mag := time.Duration(d.Magnitude) + switch d.Unit { + case "d": + dur += mag * (day) + case "m": + dur += mag * time.Minute + case "s": + dur += mag * time.Second + case "ms": + dur += mag * time.Minute + case "us": + dur += mag * time.Microsecond + case "ns": + dur += mag * time.Nanosecond + default: + return 0, errors.New("duration must be day(d), hour(h), min(m), sec(s), millisec(ms), microsec(us), or nanosec(ns)") + } + } + return dur, nil +} diff --git a/cmd/influx/setup.go b/cmd/influx/setup.go index c3ed25d56ac..509622048ae 100644 --- a/cmd/influx/setup.go +++ b/cmd/influx/setup.go @@ -25,7 +25,7 @@ var setupFlags struct { name string org string password string - retention time.Duration + retention string token string username string } @@ -41,7 +41,7 @@ func cmdSetup(f *globalFlags, opt genericCLIOpts) *cobra.Command { cmd.Flags().StringVarP(&setupFlags.org, "org", "o", "", "primary organization name") cmd.Flags().StringVarP(&setupFlags.bucket, "bucket", "b", "", "primary bucket name") 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().StringVarP(&setupFlags.retention, "retention", "r", "", "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) @@ -62,7 +62,7 @@ func cmdSetupUser(opt genericCLIOpts) *cobra.Command { cmd.Flags().StringVarP(&setupFlags.org, "org", "o", "", "primary organization name") cmd.Flags().StringVarP(&setupFlags.bucket, "bucket", "b", "", "primary bucket name") 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().StringVarP(&setupFlags.retention, "retention", "r", "", "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) @@ -220,18 +220,20 @@ func onboardingRequest() (*influxdb.OnboardingRequest, error) { func nonInteractive() (*influxdb.OnboardingRequest, error) { req := &influxdb.OnboardingRequest{ - User: setupFlags.username, - Password: setupFlags.password, - Token: setupFlags.token, - Org: setupFlags.org, - Bucket: setupFlags.bucket, - // TODO: this manipulation is required by the API, something that - // we should fixup to be a duration instead - RetentionPeriod: uint(setupFlags.retention / time.Hour), + User: setupFlags.username, + Password: setupFlags.password, + Token: setupFlags.token, + Org: setupFlags.org, + Bucket: setupFlags.bucket, + RetentionPeriod: influxdb.InfiniteRetention, } - if setupFlags.retention < 0 { - req.RetentionPeriod = influxdb.InfiniteRetention + dur, err := rawDurationToTimeDuration(setupFlags.retention) + if err != nil { + return nil, err + } + if dur > 0 { + req.RetentionPeriod = uint(dur / time.Hour) } return req, nil } @@ -267,8 +269,14 @@ func interactive() (req *influxdb.OnboardingRequest, err error) { } else { req.Bucket = getInput(ui, "Please type your primary bucket name", "") } - if setupFlags.retention >= 0 { - req.RetentionPeriod = uint(setupFlags.retention) + + dur, err := rawDurationToTimeDuration(setupFlags.retention) + if err != nil { + return nil, err + } + + if dur > 0 { + req.RetentionPeriod = uint(dur / time.Hour) } else { for { rpStr := getInput(ui, "Please type your retention period in hours.\r\nOr press ENTER for infinite.", strconv.Itoa(influxdb.InfiniteRetention)) diff --git a/task/options/options.go b/task/options/options.go index 8fa476f6044..94c41c24adc 100644 --- a/task/options/options.go +++ b/task/options/options.go @@ -79,7 +79,7 @@ func (a *Duration) UnmarshalText(text []byte) error { return nil } -// UnmarshalText marshals text into a Duration. +// MarshalText marshals text into a Duration. func (a Duration) MarshalText() ([]byte, error) { return []byte(a.String()), nil } diff --git a/task/options/strconv.go b/task/options/strconv.go index 815c9c19e77..7730fee4577 100644 --- a/task/options/strconv.go +++ b/task/options/strconv.go @@ -26,8 +26,7 @@ func ParseSignedDuration(text string) (*ast.DurationLiteral, error) { // TODO(jsternberg): This is copied from an internal package in flux to break a dependency // on the parser package where this method is exposed. // Consider exposing this properly in flux. - r, s := utf8.DecodeRuneInString(text) - if r == '-' { + if r, s := utf8.DecodeRuneInString(text); r == '-' { d, err := parseDuration(text[s:]) if err != nil { return nil, err