From a199998e2885af1cef9d37c53b22239c74ac3b89 Mon Sep 17 00:00:00 2001 From: Johnny Steenbergen Date: Fri, 10 Jan 2020 09:46:56 -0800 Subject: [PATCH] chore(influx): refactor bucket cmd into builder pattern --- cmd/influx/bucket.go | 355 ++++++++++++++++----------------- cmd/influx/bucket_test.go | 406 ++++++++++++++++++++++++++++++++++++++ cmd/influx/main.go | 23 ++- cmd/influx/pkg.go | 4 +- cmd/influx/pkg_test.go | 31 +-- 5 files changed, 606 insertions(+), 213 deletions(-) create mode 100644 cmd/influx/bucket_test.go diff --git a/cmd/influx/bucket.go b/cmd/influx/bucket.go index 3e3fdabddb8..a38d725bf5a 100644 --- a/cmd/influx/bucket.go +++ b/cmd/influx/bucket.go @@ -6,13 +6,47 @@ import ( "os" "time" - platform "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/cmd/influx/internal" "github.com/influxdata/influxdb/http" "github.com/spf13/cobra" ) -func cmdBucket() *cobra.Command { +type bucketSVCsFn func() (influxdb.BucketService, influxdb.OrganizationService, error) + +func cmdBucket(svcsFn bucketSVCsFn, opts ...genericCLIOptFn) *cobra.Command { + return newCmdBucketBuilder(svcsFn, opts...).cmdBucket() +} + +type cmdBucketBuilder struct { + genericCLIOpts + + svcFn bucketSVCsFn + + id string + headers bool + name string + description string + org organization + retention time.Duration +} + +func newCmdBucketBuilder(svcsFn bucketSVCsFn, opts ...genericCLIOptFn) *cmdBucketBuilder { + opt := genericCLIOpts{ + in: os.Stdin, + w: os.Stdout, + } + for _, o := range opts { + o(&opt) + } + + return &cmdBucketBuilder{ + genericCLIOpts: opt, + svcFn: svcsFn, + } +} + +func (b *cmdBucketBuilder) cmdBucket() *cobra.Command { cmd := &cobra.Command{ Use: "bucket", Short: "Bucket management commands", @@ -20,115 +54,136 @@ func cmdBucket() *cobra.Command { Run: seeHelp, } cmd.AddCommand( - bucketCreateCmd(), - bucketDeleteCmd(), - bucketFindCmd(), - bucketUpdateCmd(), + b.cmdCreate(), + b.cmdDelete(), + b.cmdFind(), + b.cmdUpdate(), ) return cmd } -var bucketCreateFlags struct { - name string - organization - retention time.Duration -} - -func bucketCreateCmd() *cobra.Command { +func (b *cmdBucketBuilder) cmdCreate() *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "Create bucket", - RunE: wrapCheckSetup(bucketCreateF), + RunE: wrapCheckSetup(b.cmdCreateRunEFn), + } + + opts := flagOpts{ + { + DestP: &b.name, + Flag: "name", + Short: 'n', + EnvVar: "BUCKET_NAME", + Desc: "New bucket name", + Required: true, + }, } + opts.mustRegister(cmd) - 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) + cmd.Flags().StringVarP(&b.description, "description", "d", "", "Description of bucket that will be created") + cmd.Flags().DurationVarP(&b.retention, "retention", "r", 0, "Duration in nanoseconds data will live in bucket") + b.org.register(cmd, false) return cmd } -func newBucketService() (platform.BucketService, error) { - if flags.local { - return newLocalKVService() +func (b *cmdBucketBuilder) cmdCreateRunEFn(*cobra.Command, []string) error { + if err := b.org.validOrgFlags(); err != nil { + return err } - client, err := newHTTPClient() + bktSVC, orgSVC, err := b.svcFn() if err != nil { - return nil, err + return err } - return &http.BucketService{ - Client: client, - }, nil -} - -func bucketCreateF(cmd *cobra.Command, args []string) error { - if err := bucketCreateFlags.organization.validOrgFlags(); err != nil { + bkt := &influxdb.Bucket{ + Name: b.name, + Description: b.description, + RetentionPeriod: b.retention, + } + bkt.OrgID, err = b.org.getID(orgSVC) + if err != nil { return err } - s, err := newBucketService() - if err != nil { - return fmt.Errorf("failed to initialize bucket service client: %v", err) + if err := bktSVC.CreateBucket(context.Background(), bkt); err != nil { + return fmt.Errorf("failed to create bucket: %v", err) } - b := &platform.Bucket{ - Name: bucketCreateFlags.name, - RetentionPeriod: bucketCreateFlags.retention, + w := internal.NewTabWriter(b.w) + w.WriteHeaders("ID", "Name", "Retention", "OrganizationID") + w.Write(map[string]interface{}{ + "ID": bkt.ID.String(), + "Name": bkt.Name, + "Retention": bkt.RetentionPeriod, + "OrganizationID": bkt.OrgID.String(), + }) + w.Flush() + + return nil +} + +func (b *cmdBucketBuilder) cmdDelete() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete bucket", + RunE: wrapCheckSetup(b.cmdDeleteRunEFn), } - orgSvc, err := newOrganizationService() + cmd.Flags().StringVarP(&b.id, "id", "i", "", "The bucket ID (required)") + cmd.MarkFlagRequired("id") + + return cmd +} + +func (b *cmdBucketBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) error { + bktSVC, _, err := b.svcFn() if err != nil { return err } - b.OrgID, err = bucketCreateFlags.organization.getID(orgSvc) + var id influxdb.ID + if err := id.DecodeFromString(b.id); err != nil { + return fmt.Errorf("failed to decode bucket id %q: %v", b.id, err) + } + + ctx := context.Background() + bkt, err := bktSVC.FindBucketByID(ctx, id) if err != nil { - return err + return fmt.Errorf("failed to find bucket with id %q: %v", id, err) } - if err := s.CreateBucket(context.Background(), b); err != nil { - return fmt.Errorf("failed to create bucket: %v", err) + if err := bktSVC.DeleteBucket(ctx, id); err != nil { + return fmt.Errorf("failed to delete bucket with id %q: %v", id, err) } - w := internal.NewTabWriter(os.Stdout) - w.WriteHeaders( - "ID", - "Name", - "Retention", - "OrganizationID", - ) + w := internal.NewTabWriter(b.w) + w.WriteHeaders("ID", "Name", "Retention", "OrganizationID", "Deleted") w.Write(map[string]interface{}{ - "ID": b.ID.String(), - "Name": b.Name, - "Retention": b.RetentionPeriod, - "OrganizationID": b.OrgID.String(), + "ID": bkt.ID.String(), + "Name": bkt.Name, + "Retention": bkt.RetentionPeriod, + "OrganizationID": bkt.OrgID.String(), + "Deleted": true, }) w.Flush() return nil } -var bucketFindFlags struct { - name string - id string - headers bool - organization -} - -func bucketFindCmd() *cobra.Command { +func (b *cmdBucketBuilder) cmdFind() *cobra.Command { cmd := &cobra.Command{ Use: "find", Short: "Find buckets", - RunE: wrapCheckSetup(bucketFindF), + RunE: wrapCheckSetup(b.cmdFindRunEFn), } opts := flagOpts{ { - DestP: &bucketFindFlags.name, + DestP: &b.name, Flag: "name", Short: 'n', EnvVar: "BUCKET_NAME", @@ -137,61 +192,53 @@ func bucketFindCmd() *cobra.Command { } 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) + b.org.register(cmd, false) + cmd.Flags().StringVarP(&b.id, "id", "i", "", "The bucket ID") + cmd.Flags().BoolVar(&b.headers, "headers", true, "To print the table headers; defaults true") return cmd } -func bucketFindF(cmd *cobra.Command, args []string) error { - s, err := newBucketService() - if err != nil { - return fmt.Errorf("failed to initialize bucket service client: %v", err) +func (b *cmdBucketBuilder) cmdFindRunEFn(cmd *cobra.Command, args []string) error { + if err := b.org.validOrgFlags(); err != nil { + return err } - filter := platform.BucketFilter{} - if bucketFindFlags.name != "" { - filter.Name = &bucketFindFlags.name + bktSVC, _, err := b.svcFn() + if err != nil { + return err } - if bucketFindFlags.id != "" { - id, err := platform.IDFromString(bucketFindFlags.id) + var filter influxdb.BucketFilter + if b.name != "" { + filter.Name = &b.name + } + if b.id != "" { + id, err := influxdb.IDFromString(b.id) if err != nil { - return fmt.Errorf("failed to decode bucket id %q: %v", bucketFindFlags.id, err) + return fmt.Errorf("failed to decode bucket id %q: %v", b.id, err) } filter.ID = id } - - if err := bucketFindFlags.organization.validOrgFlags(); err != nil { - return err - } - - if bucketFindFlags.organization.id != "" { - orgID, err := platform.IDFromString(bucketFindFlags.organization.id) + if b.org.id != "" { + orgID, err := influxdb.IDFromString(b.org.id) if err != nil { - return fmt.Errorf("failed to decode org id %q: %v", bucketFindFlags.organization.id, err) + return fmt.Errorf("failed to decode org id %q: %v", b.org.id, err) } filter.OrganizationID = orgID } - - if bucketFindFlags.organization.name != "" { - filter.Org = &bucketFindFlags.organization.name + if b.org.name != "" { + filter.Org = &b.org.name } - buckets, _, err := s.FindBuckets(context.Background(), filter) + buckets, _, err := bktSVC.FindBuckets(context.Background(), filter) if err != nil { return fmt.Errorf("failed to retrieve buckets: %s", err) } - w := internal.NewTabWriter(os.Stdout) - w.HideHeaders(!bucketFindFlags.headers) - w.WriteHeaders( - "ID", - "Name", - "Retention", - "OrganizationID", - ) + w := internal.NewTabWriter(b.w) + w.HideHeaders(!b.headers) + w.WriteHeaders("ID", "Name", "Retention", "OrganizationID") for _, b := range buckets { w.Write(map[string]interface{}{ "ID": b.ID.String(), @@ -205,22 +252,16 @@ func bucketFindF(cmd *cobra.Command, args []string) error { return nil } -var bucketUpdateFlags struct { - id string - name string - retention time.Duration -} - -func bucketUpdateCmd() *cobra.Command { +func (b *cmdBucketBuilder) cmdUpdate() *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "Update bucket", - RunE: wrapCheckSetup(bucketUpdateF), + RunE: wrapCheckSetup(b.cmdUpdateRunEFn), } opts := flagOpts{ { - DestP: &bucketUpdateFlags.name, + DestP: &b.name, Flag: "name", Short: 'n', EnvVar: "BUCKET_NAME", @@ -229,109 +270,61 @@ func bucketUpdateCmd() *cobra.Command { } opts.mustRegister(cmd) - cmd.Flags().StringVarP(&bucketUpdateFlags.id, "id", "i", "", "The bucket ID (required)") + 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(&bucketUpdateFlags.retention, "retention", "r", 0, "New duration data will live in bucket") + cmd.Flags().DurationVarP(&b.retention, "retention", "r", 0, "New duration data will live in bucket") return cmd } -func bucketUpdateF(cmd *cobra.Command, args []string) error { - s, err := newBucketService() +func (b *cmdBucketBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) error { + bktSVC, _, err := b.svcFn() if err != nil { - return fmt.Errorf("failed to initialize bucket service client: %v", err) + return err } - var id platform.ID - if err := id.DecodeFromString(bucketUpdateFlags.id); err != nil { - return fmt.Errorf("failed to decode bucket id %q: %v", bucketUpdateFlags.id, err) + var id influxdb.ID + if err := id.DecodeFromString(b.id); err != nil { + return fmt.Errorf("failed to decode bucket id %q: %v", b.id, err) } - update := platform.BucketUpdate{} - if bucketUpdateFlags.name != "" { - update.Name = &bucketUpdateFlags.name + var update influxdb.BucketUpdate + if b.name != "" { + update.Name = &b.name } - if bucketUpdateFlags.retention != 0 { - update.RetentionPeriod = &bucketUpdateFlags.retention + if b.description != "" { + update.Description = &b.description + } + if b.retention != 0 { + update.RetentionPeriod = &b.retention } - b, err := s.UpdateBucket(context.Background(), id, update) + bkt, err := bktSVC.UpdateBucket(context.Background(), id, update) if err != nil { return fmt.Errorf("failed to update bucket: %v", err) } - w := internal.NewTabWriter(os.Stdout) - w.WriteHeaders( - "ID", - "Name", - "Retention", - "OrganizationID", - ) + w := internal.NewTabWriter(b.w) + w.WriteHeaders("ID", "Name", "Retention", "OrganizationID") w.Write(map[string]interface{}{ - "ID": b.ID.String(), - "Name": b.Name, - "Retention": b.RetentionPeriod, - "OrganizationID": b.OrgID.String(), + "ID": bkt.ID.String(), + "Name": bkt.Name, + "Retention": bkt.RetentionPeriod, + "OrganizationID": bkt.OrgID.String(), }) w.Flush() return nil } -var bucketDeleteFlags struct { - id string -} - -func bucketDeleteF(cmd *cobra.Command, args []string) error { - s, err := newBucketService() +func newBucketSVCs() (influxdb.BucketService, influxdb.OrganizationService, error) { + httpClient, err := newHTTPClient() if err != nil { - return fmt.Errorf("failed to initialize bucket service client: %v", err) + return nil, nil, err } - var id platform.ID - if err := id.DecodeFromString(bucketDeleteFlags.id); err != nil { - return fmt.Errorf("failed to decode bucket id %q: %v", bucketDeleteFlags.id, err) - } + orgSvc := &http.OrganizationService{Client: httpClient} - ctx := context.Background() - b, err := s.FindBucketByID(ctx, id) - if err != nil { - return fmt.Errorf("failed to find bucket with id %q: %v", id, err) - } - - if err = s.DeleteBucket(ctx, id); err != nil { - return fmt.Errorf("failed to delete bucket with id %q: %v", id, err) - } - - w := internal.NewTabWriter(os.Stdout) - w.WriteHeaders( - "ID", - "Name", - "Retention", - "OrganizationID", - "Deleted", - ) - w.Write(map[string]interface{}{ - "ID": b.ID.String(), - "Name": b.Name, - "Retention": b.RetentionPeriod, - "OrganizationID": b.OrgID.String(), - "Deleted": true, - }) - w.Flush() - - return nil -} - -func bucketDeleteCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "delete", - Short: "Delete bucket", - RunE: wrapCheckSetup(bucketDeleteF), - } - - cmd.Flags().StringVar(&bucketDeleteFlags.id, "id", "i", "The bucket ID (required)") - cmd.MarkFlagRequired("id") - - return cmd + return &http.BucketService{Client: httpClient}, orgSvc, nil } diff --git a/cmd/influx/bucket_test.go b/cmd/influx/bucket_test.go new file mode 100644 index 00000000000..c60b40216c1 --- /dev/null +++ b/cmd/influx/bucket_test.go @@ -0,0 +1,406 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "os" + "reflect" + "testing" + "time" + + "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb/mock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBucket(t *testing.T) { + setViperOptions() + + orgID := influxdb.ID(9000) + + fakeSVCFn := func(svc influxdb.BucketService) bucketSVCsFn { + return func() (influxdb.BucketService, influxdb.OrganizationService, error) { + return svc, &mock.OrganizationService{ + FindOrganizationF: func(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) { + return &influxdb.Organization{ID: orgID, Name: "influxdata"}, nil + }, + }, nil + } + } + + t.Run("create", func(t *testing.T) { + tests := []struct { + name string + expectedBucket influxdb.Bucket + flags []string + envVars map[string]string + }{ + { + name: "basic just name", + flags: []string{"--name=new name", "--org=org name"}, + expectedBucket: influxdb.Bucket{ + Name: "new name", + OrgID: orgID, + }, + }, + { + name: "with description and retention period", + flags: []string{ + "--name=new name", + "--description=desc", + "--retention=1m", + "--org=org name", + }, + expectedBucket: influxdb.Bucket{ + Name: "new name", + Description: "desc", + RetentionPeriod: time.Minute, + OrgID: orgID, + }, + }, + { + name: "shorts", + flags: []string{ + "-n=new name", + "-d=desc", + "-r=1m", + "-o=org name", + }, + expectedBucket: influxdb.Bucket{ + Name: "new name", + Description: "desc", + RetentionPeriod: time.Minute, + OrgID: orgID, + }, + }, + { + name: "env vars", + flags: []string{ + "-d=desc", + "-r=1m", + "-o=org name", + }, + envVars: map[string]string{"INFLUX_BUCKET_NAME": "new name"}, + expectedBucket: influxdb.Bucket{ + Name: "new name", + Description: "desc", + RetentionPeriod: time.Minute, + OrgID: orgID, + }, + }, + } + + cmdFn := func(expectedBkt influxdb.Bucket) *cobra.Command { + svc := mock.NewBucketService() + svc.CreateBucketFn = func(ctx context.Context, bucket *influxdb.Bucket) error { + if expectedBkt != *bucket { + return fmt.Errorf("unexpected bucket;\n\twant= %+v\n\tgot= %+v", expectedBkt, *bucket) + } + return nil + } + + builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer))) + cmd := builder.cmdCreate() + cmd.RunE = builder.cmdCreateRunEFn + return cmd + } + + for _, tt := range tests { + fn := func(t *testing.T) { + defer addEnvVars(t, tt.envVars)() + + cmd := cmdFn(tt.expectedBucket) + cmd.Flags().Parse(tt.flags) + require.NoError(t, cmd.Execute()) + } + + t.Run(tt.name, fn) + } + }) + + t.Run("delete", func(t *testing.T) { + tests := []struct { + name string + expectedID influxdb.ID + flag string + }{ + { + name: "with description and retention period", + expectedID: influxdb.ID(1), + flag: "--id=", + }, + { + name: "shorts", + expectedID: influxdb.ID(1), + flag: "-i=", + }, + } + + cmdFn := func(expectedID influxdb.ID) *cobra.Command { + svc := mock.NewBucketService() + svc.FindBucketByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.Bucket, error) { + return &influxdb.Bucket{ID: id}, nil + } + svc.DeleteBucketFn = func(ctx context.Context, id influxdb.ID) error { + if expectedID != id { + return fmt.Errorf("unexpected id:\n\twant= %s\n\tgot= %s", expectedID, id) + } + return nil + } + + builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer))) + cmd := builder.cmdDelete() + cmd.RunE = builder.cmdDeleteRunEFn + return cmd + } + + for _, tt := range tests { + fn := func(t *testing.T) { + cmd := cmdFn(tt.expectedID) + idFlag := tt.flag + tt.expectedID.String() + cmd.Flags().Parse([]string{idFlag}) + require.NoError(t, cmd.Execute()) + } + + t.Run(tt.name, fn) + } + }) + + t.Run("find", func(t *testing.T) { + type called struct { + name string + id influxdb.ID + orgID influxdb.ID + org string + } + + tests := []struct { + name string + expected called + flags []string + envVars map[string]string + }{ + { + name: "org id", + flags: []string{"--org-id=" + influxdb.ID(3).String()}, + expected: called{orgID: 3}, + }, + { + name: "id", + flags: []string{ + "--id=" + influxdb.ID(2).String(), + "--org-id=" + influxdb.ID(3).String(), + }, + expected: called{ + id: 2, + orgID: 3, + }, + }, + { + name: "org", + flags: []string{"--org=rg"}, + expected: called{org: "rg"}, + }, + { + name: "name", + flags: []string{"--org=rg", "--name=name1"}, + expected: called{org: "rg", name: "name1"}, + }, + { + name: "shorts", + flags: []string{ + "-o=rg", + "-n=name1", + "-i=" + influxdb.ID(1).String(), + }, + expected: called{org: "rg", name: "name1", id: 1}, + }, + { + name: "env vars", + envVars: map[string]string{ + "INFLUX_ORG": "rg", + "INFLUX_BUCKET_NAME": "name1", + }, + flags: []string{"-i=" + influxdb.ID(1).String()}, + expected: called{org: "rg", name: "name1", id: 1}, + }, + { + name: "env vars 2", + envVars: map[string]string{ + "INFLUX_ORG_ID": influxdb.ID(2).String(), + "INFLUX_BUCKET_NAME": "name1", + }, + flags: []string{"-i=" + influxdb.ID(1).String()}, + expected: called{orgID: 2, name: "name1", id: 1}, + }, + } + + cmdFn := func() (*cobra.Command, *called) { + calls := new(called) + + svc := mock.NewBucketService() + svc.FindBucketsFn = func(ctx context.Context, f influxdb.BucketFilter, opt ...influxdb.FindOptions) ([]*influxdb.Bucket, int, error) { + if f.ID != nil { + calls.id = *f.ID + } + if f.OrganizationID != nil { + calls.orgID = *f.OrganizationID + } + if f.Name != nil { + calls.name = *f.Name + } + if f.Org != nil { + calls.org = *f.Org + } + return nil, 0, nil + } + + builder := newCmdBucketBuilder(fakeSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer))) + cmd := builder.cmdFind() + cmd.RunE = builder.cmdFindRunEFn + return cmd, calls + } + + for _, tt := range tests { + fn := func(t *testing.T) { + defer addEnvVars(t, tt.envVars)() + + cmd, calls := cmdFn() + cmd.Flags().Parse(tt.flags) + + require.NoError(t, cmd.Execute()) + assert.Equal(t, tt.expected, *calls) + } + + t.Run(tt.name, fn) + } + }) + + t.Run("update", func(t *testing.T) { + tests := []struct { + name string + expected influxdb.BucketUpdate + flags []string + envVars map[string]string + }{ + { + name: "basic just name", + flags: []string{ + "--id=" + influxdb.ID(3).String(), + "--name=new name", + }, + expected: influxdb.BucketUpdate{ + Name: strPtr("new name"), + }, + }, + { + name: "with all fields", + flags: []string{ + "--id=" + influxdb.ID(3).String(), + "--name=new name", + "--description=desc", + "--retention=1m", + }, + expected: influxdb.BucketUpdate{ + Name: strPtr("new name"), + Description: strPtr("desc"), + RetentionPeriod: durPtr(time.Minute), + }, + }, + { + name: "shorts", + flags: []string{ + "-i=" + influxdb.ID(3).String(), + "-n=new name", + "-d=desc", + "-r=1m", + }, + expected: influxdb.BucketUpdate{ + Name: strPtr("new name"), + Description: strPtr("desc"), + RetentionPeriod: durPtr(time.Minute), + }, + }, + { + name: "env var", + flags: []string{ + "-i=" + influxdb.ID(3).String(), + "-d=desc", + "-r=1m", + }, + envVars: map[string]string{"INFLUX_BUCKET_NAME": "new name"}, + expected: influxdb.BucketUpdate{ + Name: strPtr("new name"), + Description: strPtr("desc"), + RetentionPeriod: durPtr(time.Minute), + }, + }, + } + + cmdFn := func(expectedUpdate influxdb.BucketUpdate) *cobra.Command { + svc := mock.NewBucketService() + svc.UpdateBucketFn = func(ctx context.Context, id influxdb.ID, upd influxdb.BucketUpdate) (*influxdb.Bucket, error) { + if id != 3 { + return nil, fmt.Errorf("unexpecte id:\n\twant= %s\n\tgot= %s", influxdb.ID(3), id) + } + if !reflect.DeepEqual(expectedUpdate, upd) { + return nil, fmt.Errorf("unexpected bucket update;\n\twant= %+v\n\tgot= %+v", expectedUpdate, upd) + } + return &influxdb.Bucket{}, nil + } + + builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer))) + cmd := builder.cmdUpdate() + cmd.RunE = builder.cmdUpdateRunEFn + return cmd + } + + for _, tt := range tests { + fn := func(t *testing.T) { + defer addEnvVars(t, tt.envVars)() + + cmd := cmdFn(tt.expected) + cmd.Flags().Parse(tt.flags) + require.NoError(t, cmd.Execute()) + } + + t.Run(tt.name, fn) + } + }) +} + +func strPtr(s string) *string { + return &s +} + +func durPtr(d time.Duration) *time.Duration { + return &d +} + +func addEnvVars(t *testing.T, envVars map[string]string) func() { + t.Helper() + + var initialEnvVars []struct{ key, val string } + for key, val := range envVars { + if k := os.Getenv(key); k != "" { + initialEnvVars = append(initialEnvVars, struct{ key, val string }{ + key: key, + val: k, + }) + } + + require.NoError(t, os.Setenv(key, val)) + } + return func() { + for key := range envVars { + require.NoError(t, os.Unsetenv(key)) + } + + for _, envVar := range initialEnvVars { + require.NoError(t, os.Setenv(envVar.key, envVar.val)) + } + } +} diff --git a/cmd/influx/main.go b/cmd/influx/main.go index eedadb341d9..f8b8824f11c 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -50,7 +50,7 @@ func newHTTPClient() (*httpc.Client, error) { return httpClient, nil } -type genericCLIOptfn func(*genericCLIOpts) +type genericCLIOptFn func(*genericCLIOpts) type genericCLIOpts struct { in io.Reader @@ -63,13 +63,13 @@ func (o genericCLIOpts) newCmd(use string) *cobra.Command { return cmd } -func in(r io.Reader) genericCLIOptfn { +func in(r io.Reader) genericCLIOptFn { return func(o *genericCLIOpts) { o.in = r } } -func out(w io.Writer) genericCLIOptfn { +func out(w io.Writer) genericCLIOptFn { return func(o *genericCLIOpts) { o.w = w } @@ -105,7 +105,7 @@ func influxCmd() *cobra.Command { cmd.AddCommand( cmdAuth(), - cmdBucket(), + cmdBucket(newBucketSVCs), cmdDelete(), cmdOrganization(), cmdPing(), @@ -348,3 +348,18 @@ func setViperOptions() { viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) } + +func newBucketService() (influxdb.BucketService, error) { + if flags.local { + return newLocalKVService() + } + + client, err := newHTTPClient() + if err != nil { + return nil, err + } + + return &http.BucketService{ + Client: client, + }, nil +} diff --git a/cmd/influx/pkg.go b/cmd/influx/pkg.go index 3bf1a2cf579..381c851728d 100644 --- a/cmd/influx/pkg.go +++ b/cmd/influx/pkg.go @@ -29,7 +29,7 @@ import ( type pkgSVCsFn func() (pkger.SVC, influxdb.OrganizationService, error) -func cmdPkg(svcFn pkgSVCsFn, opts ...genericCLIOptfn) *cobra.Command { +func cmdPkg(svcFn pkgSVCsFn, opts ...genericCLIOptFn) *cobra.Command { return newCmdPkgBuilder(svcFn, opts...).cmdPkg() } @@ -63,7 +63,7 @@ type cmdPkgBuilder struct { } } -func newCmdPkgBuilder(svcFn pkgSVCsFn, opts ...genericCLIOptfn) *cmdPkgBuilder { +func newCmdPkgBuilder(svcFn pkgSVCsFn, opts ...genericCLIOptFn) *cmdPkgBuilder { opt := genericCLIOpts{ in: os.Stdin, w: os.Stdout, diff --git a/cmd/influx/pkg_test.go b/cmd/influx/pkg_test.go index 6b2b46d6efc..fa7d3667613 100644 --- a/cmd/influx/pkg_test.go +++ b/cmd/influx/pkg_test.go @@ -161,7 +161,7 @@ func Test_Pkg(t *testing.T) { {name: "description", val: "new desc"}, {name: "version", val: "new version"}, }, - envVars: []struct{ key, val string }{{key: "INFLUX_ORG", val: "influxdata"}}, + envVars: map[string]string{"INFLUX_ORG": "influxdata"}, }, expectedMeta: pkger.Metadata{ Name: "new name", @@ -179,7 +179,7 @@ func Test_Pkg(t *testing.T) { {name: "description", val: "new desc"}, {name: "version", val: "new version"}, }, - envVars: []struct{ key, val string }{{key: "INFLUX_ORG_ID", val: expectedOrgID.String()}}, + envVars: map[string]string{"INFLUX_ORG_ID": expectedOrgID.String()}, }, expectedMeta: pkger.Metadata{ Name: "new name", @@ -518,41 +518,20 @@ type pkgFileArgs struct { filename string encoding pkger.Encoding flags []flagArg - envVars []struct { - key, val string - } + envVars map[string]string } func testPkgWrites(t *testing.T, newCmdFn func() *cobra.Command, args pkgFileArgs, assertFn func(t *testing.T, pkg *pkger.Pkg)) { t.Helper() + defer addEnvVars(t, args.envVars)() + wrappedCmdFn := func() *cobra.Command { cmd := newCmdFn() cmd.SetArgs([]string{}) // clears mess from test runner coming into cobra cli via stdin return cmd } - var initialEnvVars []struct{ key, val string } - for _, envVar := range args.envVars { - if k := os.Getenv(envVar.key); k != "" { - initialEnvVars = append(initialEnvVars, struct{ key, val string }{ - key: envVar.key, - val: k, - }) - } - - require.NoError(t, os.Setenv(envVar.key, envVar.val)) - } - - defer func() { - for _, envVar := range args.envVars { - require.NoError(t, os.Unsetenv(envVar.key)) - } - - for _, envVar := range initialEnvVars { - require.NoError(t, os.Setenv(envVar.key, envVar.val)) - } - }() t.Run(path.Join(args.name, "file"), testPkgWritesFile(wrappedCmdFn, args, assertFn)) t.Run(path.Join(args.name, "buffer"), testPkgWritesToBuffer(wrappedCmdFn, args, assertFn)) }