diff --git a/internal/cmd/base/labels.go b/internal/cmd/base/labels.go index d5472770..71897c0a 100644 --- a/internal/cmd/base/labels.go +++ b/internal/cmd/base/labels.go @@ -27,9 +27,9 @@ type LabelCmds struct { // AddCobraCommand creates a command that can be registered with cobra. func (lc *LabelCmds) AddCobraCommand(s state.State) *cobra.Command { cmd := &cobra.Command{ - Use: fmt.Sprintf("add-label [FLAGS] %s LABEL", strings.ToUpper(lc.ResourceNameSingular)), + Use: fmt.Sprintf("add-label [FLAGS] %s LABEL...", strings.ToUpper(lc.ResourceNameSingular)), Short: lc.ShortDescriptionAdd, - Args: cobra.ExactArgs(2), + Args: cobra.MinimumNArgs(2), ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(lc.NameSuggestions(s.Client()))), TraverseChildren: true, DisableFlagsInUseLine: true, @@ -56,26 +56,31 @@ func (lc *LabelCmds) RunAdd(s state.State, cmd *cobra.Command, args []string) er labels = map[string]string{} } - key, val := util.SplitLabelVars(args[1]) + var keys []string + for _, label := range args[1:] { + key, val := util.SplitLabelVars(label) + keys = append(keys, key) - if _, ok := labels[key]; ok && !overwrite { - return fmt.Errorf("label %s on %s %d already exists", key, lc.ResourceNameSingular, id) - } + if _, ok := labels[key]; ok && !overwrite { + return fmt.Errorf("label %s on %s %d already exists", key, lc.ResourceNameSingular, id) + } - labels[key] = val + labels[key] = val + } if err := lc.SetLabels(s, id, labels); err != nil { return err } - cmd.Printf("Label %s added to %s %d\n", key, lc.ResourceNameSingular, id) + cmd.Printf("Label(s) %s added to %s %d\n", strings.Join(keys, ", "), lc.ResourceNameSingular, id) return nil } func validateAddLabel(_ *cobra.Command, args []string) error { - label := util.SplitLabel(args[1]) - if len(label) != 2 { - return fmt.Errorf("invalid label: %s", args[1]) + for _, label := range args[1:] { + if len(util.SplitLabel(label)) != 2 { + return fmt.Errorf("invalid label: %s", label) + } } return nil @@ -84,9 +89,9 @@ func validateAddLabel(_ *cobra.Command, args []string) error { // RemoveCobraCommand creates a command that can be registered with cobra. func (lc *LabelCmds) RemoveCobraCommand(s state.State) *cobra.Command { cmd := &cobra.Command{ - Use: fmt.Sprintf("remove-label [FLAGS] %s LABEL", strings.ToUpper(lc.ResourceNameSingular)), + Use: fmt.Sprintf("remove-label [FLAGS] %s LABEL...", strings.ToUpper(lc.ResourceNameSingular)), Short: lc.ShortDescriptionRemove, - Args: cobra.RangeArgs(1, 2), + Args: cobra.MinimumNArgs(1), ValidArgsFunction: cmpl.SuggestArgs( cmpl.SuggestCandidatesF(lc.NameSuggestions(s.Client())), cmpl.SuggestCandidatesCtx(func(_ *cobra.Command, args []string) []string { @@ -120,11 +125,12 @@ func (lc *LabelCmds) RunRemove(s state.State, cmd *cobra.Command, args []string) if all { labels = make(map[string]string) } else { - key := args[1] - if _, ok := labels[key]; !ok { - return fmt.Errorf("label %s on %s %d does not exist", key, lc.ResourceNameSingular, id) + for _, key := range args[1:] { + if _, ok := labels[key]; !ok { + return fmt.Errorf("label %s on %s %d does not exist", key, lc.ResourceNameSingular, id) + } + delete(labels, key) } - delete(labels, key) } if err := lc.SetLabels(s, id, labels); err != nil { @@ -134,7 +140,7 @@ func (lc *LabelCmds) RunRemove(s state.State, cmd *cobra.Command, args []string) if all { cmd.Printf("All labels removed from %s %d\n", lc.ResourceNameSingular, id) } else { - cmd.Printf("Label %s removed from %s %d\n", args[1], lc.ResourceNameSingular, id) + cmd.Printf("Label(s) %s removed from %s %d\n", strings.Join(args[1:], ", "), lc.ResourceNameSingular, id) } return nil @@ -143,10 +149,10 @@ func (lc *LabelCmds) RunRemove(s state.State, cmd *cobra.Command, args []string) func validateRemoveLabel(cmd *cobra.Command, args []string) error { all, _ := cmd.Flags().GetBool("all") - if all && len(args) == 2 { + if all && len(args) > 1 { return errors.New("must not specify a label key when using --all/-a") } - if !all && len(args) != 2 { + if !all && len(args) <= 1 { return errors.New("must specify a label key when not using --all/-a") } diff --git a/internal/cmd/certificate/labels_test.go b/internal/cmd/certificate/labels_test.go index d50cf046..bf131064 100644 --- a/internal/cmd/certificate/labels_test.go +++ b/internal/cmd/certificate/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to certificate 123\n" + expOut := "Label(s) key added to certificate 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from certificate 123\n" + expOut := "Label(s) key removed from certificate 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/firewall/labels_test.go b/internal/cmd/firewall/labels_test.go index 9e55df3f..1da9c3bf 100644 --- a/internal/cmd/firewall/labels_test.go +++ b/internal/cmd/firewall/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to firewall 123\n" + expOut := "Label(s) key added to firewall 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from firewall 123\n" + expOut := "Label(s) key removed from firewall 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/floatingip/labels_test.go b/internal/cmd/floatingip/labels_test.go index 1fc1a980..15488fb6 100644 --- a/internal/cmd/floatingip/labels_test.go +++ b/internal/cmd/floatingip/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to Floating IP 123\n" + expOut := "Label(s) key added to Floating IP 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from Floating IP 123\n" + expOut := "Label(s) key removed from Floating IP 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/image/labels_test.go b/internal/cmd/image/labels_test.go index d7c70c70..06f3000b 100644 --- a/internal/cmd/image/labels_test.go +++ b/internal/cmd/image/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to image 123\n" + expOut := "Label(s) key added to image 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from image 123\n" + expOut := "Label(s) key removed from image 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/loadbalancer/labels_test.go b/internal/cmd/loadbalancer/labels_test.go index 9ea8dc80..51690579 100644 --- a/internal/cmd/loadbalancer/labels_test.go +++ b/internal/cmd/loadbalancer/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to Load Balancer 123\n" + expOut := "Label(s) key added to Load Balancer 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from Load Balancer 123\n" + expOut := "Label(s) key removed from Load Balancer 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/network/labels_test.go b/internal/cmd/network/labels_test.go index 52058b22..e2705630 100644 --- a/internal/cmd/network/labels_test.go +++ b/internal/cmd/network/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to Network 123\n" + expOut := "Label(s) key added to Network 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from Network 123\n" + expOut := "Label(s) key removed from Network 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/placementgroup/labels_test.go b/internal/cmd/placementgroup/labels_test.go index 83c3f53f..c2e3c1a4 100644 --- a/internal/cmd/placementgroup/labels_test.go +++ b/internal/cmd/placementgroup/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to placement group 123\n" + expOut := "Label(s) key added to placement group 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from placement group 123\n" + expOut := "Label(s) key removed from placement group 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/primaryip/labels_test.go b/internal/cmd/primaryip/labels_test.go index 4aa5ab77..19e54649 100644 --- a/internal/cmd/primaryip/labels_test.go +++ b/internal/cmd/primaryip/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to primary-ip 123\n" + expOut := "Label(s) key added to primary-ip 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from primary-ip 123\n" + expOut := "Label(s) key removed from primary-ip 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/server/labels_test.go b/internal/cmd/server/labels_test.go index 03d22c53..5fcaec37 100644 --- a/internal/cmd/server/labels_test.go +++ b/internal/cmd/server/labels_test.go @@ -29,7 +29,33 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to server 123\n" + expOut := "Label(s) key added to server 123\n" + + assert.NoError(t, err) + assert.Equal(t, expOut, out) +} + +func TestMultiLabelAdd(t *testing.T) { + fx := testutil.NewFixture(t) + defer fx.Finish() + + cmd := LabelCmds.AddCobraCommand(fx.State()) + fx.ExpectEnsureToken() + + fx.Client.ServerClient.EXPECT(). + Get(gomock.Any(), "123"). + Return(&hcloud.Server{ID: 123}, nil, nil) + fx.Client.ServerClient.EXPECT(). + Update(gomock.Any(), &hcloud.Server{ID: 123}, hcloud.ServerUpdateOpts{ + Labels: map[string]string{ + "foo": "bar", + "baz": "qux", + }, + }) + + out, _, err := fx.Run(cmd, []string{"123", "foo=bar", "baz=qux"}) + + expOut := "Label(s) foo, baz added to server 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +83,39 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from server 123\n" + expOut := "Label(s) key removed from server 123\n" + + assert.NoError(t, err) + assert.Equal(t, expOut, out) +} + +func TestMultiLabelRemove(t *testing.T) { + fx := testutil.NewFixture(t) + defer fx.Finish() + + cmd := LabelCmds.RemoveCobraCommand(fx.State()) + fx.ExpectEnsureToken() + + fx.Client.ServerClient.EXPECT(). + Get(gomock.Any(), "123"). + Return(&hcloud.Server{ + ID: 123, + Labels: map[string]string{ + "key": "value", + "foo": "bar", + "baz": "qux", + }, + }, nil, nil) + fx.Client.ServerClient.EXPECT(). + Update(gomock.Any(), &hcloud.Server{ID: 123}, hcloud.ServerUpdateOpts{ + Labels: map[string]string{ + "key": "value", + }, + }) + + out, _, err := fx.Run(cmd, []string{"123", "foo", "baz"}) + + expOut := "Label(s) foo, baz removed from server 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/sshkey/labels_test.go b/internal/cmd/sshkey/labels_test.go index d293ee6f..c3f20fe0 100644 --- a/internal/cmd/sshkey/labels_test.go +++ b/internal/cmd/sshkey/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to SSH Key 123\n" + expOut := "Label(s) key added to SSH Key 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from SSH Key 123\n" + expOut := "Label(s) key removed from SSH Key 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) diff --git a/internal/cmd/volume/labels_test.go b/internal/cmd/volume/labels_test.go index 6bc15220..7aa7ccc0 100644 --- a/internal/cmd/volume/labels_test.go +++ b/internal/cmd/volume/labels_test.go @@ -29,7 +29,7 @@ func TestLabelAdd(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key=value"}) - expOut := "Label key added to Volume 123\n" + expOut := "Label(s) key added to Volume 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out) @@ -57,7 +57,7 @@ func TestLabelRemove(t *testing.T) { out, _, err := fx.Run(cmd, []string{"123", "key"}) - expOut := "Label key removed from Volume 123\n" + expOut := "Label(s) key removed from Volume 123\n" assert.NoError(t, err) assert.Equal(t, expOut, out)