From 7e728a76829fa97324207621d23b4a4c7de45d24 Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:56:31 +0100 Subject: [PATCH 1/2] refactor: split up label command into separate Fetch/GetLabels/GetIDOrName --- internal/cmd/base/labels.go | 28 ++++++++++------- internal/cmd/certificate/labels.go | 22 +++++++++---- internal/cmd/certificate/labels_test.go | 16 +++++----- internal/cmd/firewall/labels.go | 22 +++++++++---- internal/cmd/firewall/labels_test.go | 16 +++++----- internal/cmd/floatingip/labels.go | 22 +++++++++---- internal/cmd/floatingip/labels_test.go | 16 +++++----- internal/cmd/image/labels.go | 23 +++++++++----- internal/cmd/image/labels_test.go | 16 +++++----- internal/cmd/loadbalancer/labels.go | 22 +++++++++---- internal/cmd/loadbalancer/labels_test.go | 16 +++++----- internal/cmd/network/labels.go | 22 +++++++++---- internal/cmd/network/labels_test.go | 16 +++++----- internal/cmd/placementgroup/labels.go | 22 +++++++++---- internal/cmd/placementgroup/labels_test.go | 16 +++++----- internal/cmd/primaryip/labels.go | 22 +++++++++---- internal/cmd/primaryip/labels_test.go | 16 +++++----- internal/cmd/server/labels.go | 22 +++++++++---- internal/cmd/server/labels_test.go | 36 ++++++++++++---------- internal/cmd/sshkey/labels.go | 22 +++++++++---- internal/cmd/sshkey/labels_test.go | 16 +++++----- internal/cmd/volume/labels.go | 22 +++++++++---- internal/cmd/volume/labels_test.go | 22 +++++++------ 23 files changed, 307 insertions(+), 166 deletions(-) diff --git a/internal/cmd/base/labels.go b/internal/cmd/base/labels.go index 5feadeff..3b487690 100644 --- a/internal/cmd/base/labels.go +++ b/internal/cmd/base/labels.go @@ -20,8 +20,10 @@ type LabelCmds struct { ShortDescriptionRemove string NameSuggestions func(client hcapi2.Client) func() []string LabelKeySuggestions func(client hcapi2.Client) func(idOrName string) []string - FetchLabels func(s state.State, idOrName string) (map[string]string, int64, error) - SetLabels func(s state.State, id int64, labels map[string]string) error + Fetch func(s state.State, idOrName string) (any, error) + SetLabels func(s state.State, resource any, labels map[string]string) error + GetLabels func(resource any) map[string]string + GetIDOrName func(resource any) string } // AddCobraCommand creates a command that can be registered with cobra. @@ -47,11 +49,13 @@ func (lc *LabelCmds) RunAdd(s state.State, cmd *cobra.Command, args []string) er overwrite, _ := cmd.Flags().GetBool("overwrite") idOrName := args[0] - labels, id, err := lc.FetchLabels(s, idOrName) + resource, err := lc.Fetch(s, idOrName) if err != nil { return err } + labels, idOrName := lc.GetLabels(resource), lc.GetIDOrName(resource) + if labels == nil { labels = map[string]string{} } @@ -62,17 +66,17 @@ func (lc *LabelCmds) RunAdd(s state.State, cmd *cobra.Command, args []string) er keys = append(keys, key) if _, ok := labels[key]; ok && !overwrite { - return fmt.Errorf("label %s on %s %d already exists", key, lc.ResourceNameSingular, id) + return fmt.Errorf("label %s on %s %s already exists", key, lc.ResourceNameSingular, idOrName) } labels[key] = val } - if err := lc.SetLabels(s, id, labels); err != nil { + if err := lc.SetLabels(s, resource, labels); err != nil { return err } - cmd.Printf("Label(s) %s added to %s %d\n", strings.Join(keys, ", "), lc.ResourceNameSingular, id) + cmd.Printf("Label(s) %s added to %s %s\n", strings.Join(keys, ", "), lc.ResourceNameSingular, idOrName) return nil } @@ -117,30 +121,32 @@ func (lc *LabelCmds) RunRemove(s state.State, cmd *cobra.Command, args []string) all, _ := cmd.Flags().GetBool("all") idOrName := args[0] - labels, id, err := lc.FetchLabels(s, idOrName) + resource, err := lc.Fetch(s, idOrName) if err != nil { return err } + labels, idOrName := lc.GetLabels(resource), lc.GetIDOrName(resource) + if all { labels = make(map[string]string) } else { 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) + return fmt.Errorf("label %s on %s %s does not exist", key, lc.ResourceNameSingular, idOrName) } delete(labels, key) } } - if err := lc.SetLabels(s, id, labels); err != nil { + if err := lc.SetLabels(s, resource, labels); err != nil { return err } if all { - cmd.Printf("All labels removed from %s %d\n", lc.ResourceNameSingular, id) + cmd.Printf("All labels removed from %s %s\n", lc.ResourceNameSingular, idOrName) } else { - cmd.Printf("Label(s) %s removed from %s %d\n", strings.Join(args[1:], ", "), lc.ResourceNameSingular, id) + cmd.Printf("Label(s) %s removed from %s %s\n", strings.Join(args[1:], ", "), lc.ResourceNameSingular, idOrName) } return nil diff --git a/internal/cmd/certificate/labels.go b/internal/cmd/certificate/labels.go index 60d1305d..44069552 100644 --- a/internal/cmd/certificate/labels.go +++ b/internal/cmd/certificate/labels.go @@ -2,6 +2,7 @@ package certificate import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from an certificate", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Certificate().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Certificate().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { certificate, _, err := s.Client().Certificate().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if certificate == nil { - return nil, 0, fmt.Errorf("certificate not found: %s", idOrName) + return nil, fmt.Errorf("certificate not found: %s", idOrName) } - return certificate.Labels, certificate.ID, nil + return certificate, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + cert := resource.(*hcloud.Certificate) opts := hcloud.CertificateUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Certificate().Update(s, &hcloud.Certificate{ID: id}, opts) + _, _, err := s.Client().Certificate().Update(s, cert, opts) return err }, + GetLabels: func(resource any) map[string]string { + cert := resource.(*hcloud.Certificate) + return cert.Labels + }, + GetIDOrName: func(resource any) string { + cert := resource.(*hcloud.Certificate) + return strconv.FormatInt(cert.ID, 10) + }, } diff --git a/internal/cmd/certificate/labels_test.go b/internal/cmd/certificate/labels_test.go index 08c63c08..bd456864 100644 --- a/internal/cmd/certificate/labels_test.go +++ b/internal/cmd/certificate/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := certificate.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + cert := &hcloud.Certificate{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.CertificateClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Certificate{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(cert, nil, nil) fx.Client.CertificateClient.EXPECT(). - Update(gomock.Any(), &hcloud.Certificate{ID: 123}, hcloud.CertificateUpdateOpts{ + Update(gomock.Any(), cert, hcloud.CertificateUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/firewall/labels.go b/internal/cmd/firewall/labels.go index 4c1f9883..f73a297a 100644 --- a/internal/cmd/firewall/labels.go +++ b/internal/cmd/firewall/labels.go @@ -2,6 +2,7 @@ package firewall import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from an firewall", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Firewall().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Firewall().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { firewall, _, err := s.Client().Firewall().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if firewall == nil { - return nil, 0, fmt.Errorf("firewall not found: %s", idOrName) + return nil, fmt.Errorf("firewall not found: %s", idOrName) } - return firewall.Labels, firewall.ID, nil + return firewall, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + firewall := resource.(*hcloud.Firewall) opts := hcloud.FirewallUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Firewall().Update(s, &hcloud.Firewall{ID: id}, opts) + _, _, err := s.Client().Firewall().Update(s, firewall, opts) return err }, + GetLabels: func(resource any) map[string]string { + firewall := resource.(*hcloud.Firewall) + return firewall.Labels + }, + GetIDOrName: func(resource any) string { + firewall := resource.(*hcloud.Firewall) + return strconv.FormatInt(firewall.ID, 10) + }, } diff --git a/internal/cmd/firewall/labels_test.go b/internal/cmd/firewall/labels_test.go index e07e1b0b..e5b68e24 100644 --- a/internal/cmd/firewall/labels_test.go +++ b/internal/cmd/firewall/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := firewall.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + fw := &hcloud.Firewall{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.FirewallClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Firewall{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(fw, nil, nil) fx.Client.FirewallClient.EXPECT(). - Update(gomock.Any(), &hcloud.Firewall{ID: 123}, hcloud.FirewallUpdateOpts{ + Update(gomock.Any(), fw, hcloud.FirewallUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/floatingip/labels.go b/internal/cmd/floatingip/labels.go index a63a7598..c0f30dfb 100644 --- a/internal/cmd/floatingip/labels.go +++ b/internal/cmd/floatingip/labels.go @@ -2,6 +2,7 @@ package floatingip import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from an Floating IP", NameSuggestions: func(c hcapi2.Client) func() []string { return c.FloatingIP().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.FloatingIP().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { floatingIP, _, err := s.Client().FloatingIP().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if floatingIP == nil { - return nil, 0, fmt.Errorf("floating IP not found: %s", idOrName) + return nil, fmt.Errorf("floating IP not found: %s", idOrName) } - return floatingIP.Labels, floatingIP.ID, nil + return floatingIP, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + floatingIP := resource.(*hcloud.FloatingIP) opts := hcloud.FloatingIPUpdateOpts{ Labels: labels, } - _, _, err := s.Client().FloatingIP().Update(s, &hcloud.FloatingIP{ID: id}, opts) + _, _, err := s.Client().FloatingIP().Update(s, floatingIP, opts) return err }, + GetLabels: func(resource any) map[string]string { + floatingIP := resource.(*hcloud.FloatingIP) + return floatingIP.Labels + }, + GetIDOrName: func(resource any) string { + floatingIP := resource.(*hcloud.FloatingIP) + return strconv.FormatInt(floatingIP.ID, 10) + }, } diff --git a/internal/cmd/floatingip/labels_test.go b/internal/cmd/floatingip/labels_test.go index abda87d9..1015d951 100644 --- a/internal/cmd/floatingip/labels_test.go +++ b/internal/cmd/floatingip/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := floatingip.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + floatingIP := &hcloud.FloatingIP{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.FloatingIPClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.FloatingIP{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(floatingIP, nil, nil) fx.Client.FloatingIPClient.EXPECT(). - Update(gomock.Any(), &hcloud.FloatingIP{ID: 123}, hcloud.FloatingIPUpdateOpts{ + Update(gomock.Any(), floatingIP, hcloud.FloatingIPUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/image/labels.go b/internal/cmd/image/labels.go index 1c4f4640..aa046029 100644 --- a/internal/cmd/image/labels.go +++ b/internal/cmd/image/labels.go @@ -16,25 +16,34 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from an image", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Image().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Image().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { id, err := strconv.ParseInt(idOrName, 10, 64) if err != nil { - return nil, 0, fmt.Errorf("invalid snapshot or backup ID %q", idOrName) + return nil, fmt.Errorf("invalid snapshot or backup ID %q", idOrName) } image, _, err := s.Client().Image().GetByID(s, id) if err != nil { - return nil, 0, err + return nil, err } if image == nil { - return nil, 0, fmt.Errorf("image not found: %s", idOrName) + return nil, fmt.Errorf("image not found: %s", idOrName) } - return image.Labels, image.ID, nil + return image, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + image := resource.(*hcloud.Image) opts := hcloud.ImageUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Image().Update(s, &hcloud.Image{ID: id}, opts) + _, _, err := s.Client().Image().Update(s, image, opts) return err }, + GetLabels: func(resource any) map[string]string { + image := resource.(*hcloud.Image) + return image.Labels + }, + GetIDOrName: func(resource any) string { + image := resource.(*hcloud.Image) + return strconv.FormatInt(image.ID, 10) + }, } diff --git a/internal/cmd/image/labels_test.go b/internal/cmd/image/labels_test.go index 33f5a065..322e07b3 100644 --- a/internal/cmd/image/labels_test.go +++ b/internal/cmd/image/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := image.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + img := &hcloud.Image{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.ImageClient.EXPECT(). GetByID(gomock.Any(), int64(123)). - Return(&hcloud.Image{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(img, nil, nil) fx.Client.ImageClient.EXPECT(). - Update(gomock.Any(), &hcloud.Image{ID: 123}, hcloud.ImageUpdateOpts{ + Update(gomock.Any(), img, hcloud.ImageUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/loadbalancer/labels.go b/internal/cmd/loadbalancer/labels.go index c7c19d74..359b9963 100644 --- a/internal/cmd/loadbalancer/labels.go +++ b/internal/cmd/loadbalancer/labels.go @@ -2,6 +2,7 @@ package loadbalancer import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a Load Balancer", NameSuggestions: func(c hcapi2.Client) func() []string { return c.LoadBalancer().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.LoadBalancer().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { loadBalancer, _, err := s.Client().LoadBalancer().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if loadBalancer == nil { - return nil, 0, fmt.Errorf("load balancer not found: %s", idOrName) + return nil, fmt.Errorf("load balancer not found: %s", idOrName) } - return loadBalancer.Labels, loadBalancer.ID, nil + return loadBalancer, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + loadBalancer := resource.(*hcloud.LoadBalancer) opts := hcloud.LoadBalancerUpdateOpts{ Labels: labels, } - _, _, err := s.Client().LoadBalancer().Update(s, &hcloud.LoadBalancer{ID: id}, opts) + _, _, err := s.Client().LoadBalancer().Update(s, loadBalancer, opts) return err }, + GetLabels: func(resource any) map[string]string { + loadBalancer := resource.(*hcloud.LoadBalancer) + return loadBalancer.Labels + }, + GetIDOrName: func(resource any) string { + loadBalancer := resource.(*hcloud.LoadBalancer) + return strconv.FormatInt(loadBalancer.ID, 10) + }, } diff --git a/internal/cmd/loadbalancer/labels_test.go b/internal/cmd/loadbalancer/labels_test.go index c1cd356c..bf843832 100644 --- a/internal/cmd/loadbalancer/labels_test.go +++ b/internal/cmd/loadbalancer/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := loadbalancer.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + lb := &hcloud.LoadBalancer{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.LoadBalancerClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.LoadBalancer{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(lb, nil, nil) fx.Client.LoadBalancerClient.EXPECT(). - Update(gomock.Any(), &hcloud.LoadBalancer{ID: 123}, hcloud.LoadBalancerUpdateOpts{ + Update(gomock.Any(), lb, hcloud.LoadBalancerUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/network/labels.go b/internal/cmd/network/labels.go index 415d70ae..0cb207c5 100644 --- a/internal/cmd/network/labels.go +++ b/internal/cmd/network/labels.go @@ -2,6 +2,7 @@ package network import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a Network", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Network().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Network().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { network, _, err := s.Client().Network().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if network == nil { - return nil, 0, fmt.Errorf("network not found: %s", idOrName) + return nil, fmt.Errorf("network not found: %s", idOrName) } - return network.Labels, network.ID, nil + return network, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + network := resource.(*hcloud.Network) opts := hcloud.NetworkUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Network().Update(s, &hcloud.Network{ID: id}, opts) + _, _, err := s.Client().Network().Update(s, network, opts) return err }, + GetLabels: func(resource any) map[string]string { + network := resource.(*hcloud.Network) + return network.Labels + }, + GetIDOrName: func(resource any) string { + network := resource.(*hcloud.Network) + return strconv.FormatInt(network.ID, 10) + }, } diff --git a/internal/cmd/network/labels_test.go b/internal/cmd/network/labels_test.go index 85894ff5..33e36993 100644 --- a/internal/cmd/network/labels_test.go +++ b/internal/cmd/network/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := network.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + net := &hcloud.Network{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.NetworkClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Network{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(net, nil, nil) fx.Client.NetworkClient.EXPECT(). - Update(gomock.Any(), &hcloud.Network{ID: 123}, hcloud.NetworkUpdateOpts{ + Update(gomock.Any(), net, hcloud.NetworkUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/placementgroup/labels.go b/internal/cmd/placementgroup/labels.go index fc6dfe2a..68ddb6d2 100644 --- a/internal/cmd/placementgroup/labels.go +++ b/internal/cmd/placementgroup/labels.go @@ -2,6 +2,7 @@ package placementgroup import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a placement group", NameSuggestions: func(c hcapi2.Client) func() []string { return c.PlacementGroup().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.PlacementGroup().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { placementGroup, _, err := s.Client().PlacementGroup().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if placementGroup == nil { - return nil, 0, fmt.Errorf("placement group not found: %s", idOrName) + return nil, fmt.Errorf("placement group not found: %s", idOrName) } - return placementGroup.Labels, placementGroup.ID, nil + return placementGroup, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + placementGroup := resource.(*hcloud.PlacementGroup) opts := hcloud.PlacementGroupUpdateOpts{ Labels: labels, } - _, _, err := s.Client().PlacementGroup().Update(s, &hcloud.PlacementGroup{ID: id}, opts) + _, _, err := s.Client().PlacementGroup().Update(s, placementGroup, opts) return err }, + GetLabels: func(resource any) map[string]string { + placementGroup := resource.(*hcloud.PlacementGroup) + return placementGroup.Labels + }, + GetIDOrName: func(resource any) string { + placementGroup := resource.(*hcloud.PlacementGroup) + return strconv.FormatInt(placementGroup.ID, 10) + }, } diff --git a/internal/cmd/placementgroup/labels_test.go b/internal/cmd/placementgroup/labels_test.go index b2dcf6a5..adf2b6e5 100644 --- a/internal/cmd/placementgroup/labels_test.go +++ b/internal/cmd/placementgroup/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := placementgroup.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + pg := &hcloud.PlacementGroup{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.PlacementGroupClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.PlacementGroup{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(pg, nil, nil) fx.Client.PlacementGroupClient.EXPECT(). - Update(gomock.Any(), &hcloud.PlacementGroup{ID: 123}, hcloud.PlacementGroupUpdateOpts{ + Update(gomock.Any(), pg, hcloud.PlacementGroupUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/primaryip/labels.go b/internal/cmd/primaryip/labels.go index 5a84f92d..fc224a8a 100644 --- a/internal/cmd/primaryip/labels.go +++ b/internal/cmd/primaryip/labels.go @@ -2,6 +2,7 @@ package primaryip import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a Primary IP", NameSuggestions: func(c hcapi2.Client) func() []string { return c.PrimaryIP().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.PrimaryIP().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { primaryIP, _, err := s.Client().PrimaryIP().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if primaryIP == nil { - return nil, 0, fmt.Errorf("primaryIP not found: %s", idOrName) + return nil, fmt.Errorf("primaryIP not found: %s", idOrName) } - return primaryIP.Labels, primaryIP.ID, nil + return primaryIP, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + primaryIP := resource.(*hcloud.PrimaryIP) opts := hcloud.PrimaryIPUpdateOpts{ Labels: &labels, } - _, _, err := s.Client().PrimaryIP().Update(s, &hcloud.PrimaryIP{ID: id}, opts) + _, _, err := s.Client().PrimaryIP().Update(s, primaryIP, opts) return err }, + GetLabels: func(resource any) map[string]string { + primaryIP := resource.(*hcloud.PrimaryIP) + return primaryIP.Labels + }, + GetIDOrName: func(resource any) string { + primaryIP := resource.(*hcloud.PrimaryIP) + return strconv.FormatInt(primaryIP.ID, 10) + }, } diff --git a/internal/cmd/primaryip/labels_test.go b/internal/cmd/primaryip/labels_test.go index b0c662fb..d77e9144 100644 --- a/internal/cmd/primaryip/labels_test.go +++ b/internal/cmd/primaryip/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := primaryip.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + primaryIP := &hcloud.PrimaryIP{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.PrimaryIPClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.PrimaryIP{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(primaryIP, nil, nil) fx.Client.PrimaryIPClient.EXPECT(). - Update(gomock.Any(), &hcloud.PrimaryIP{ID: 123}, hcloud.PrimaryIPUpdateOpts{ + Update(gomock.Any(), primaryIP, hcloud.PrimaryIPUpdateOpts{ Labels: hcloud.Ptr(make(map[string]string)), }) diff --git a/internal/cmd/server/labels.go b/internal/cmd/server/labels.go index ecb5ede0..3033a15e 100644 --- a/internal/cmd/server/labels.go +++ b/internal/cmd/server/labels.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a server", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Server().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Server().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { server, _, err := s.Client().Server().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if server == nil { - return nil, 0, fmt.Errorf("server not found: %s", idOrName) + return nil, fmt.Errorf("server not found: %s", idOrName) } - return server.Labels, server.ID, nil + return server, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + server := resource.(*hcloud.Server) opts := hcloud.ServerUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Server().Update(s, &hcloud.Server{ID: id}, opts) + _, _, err := s.Client().Server().Update(s, server, opts) return err }, + GetLabels: func(resource any) map[string]string { + server := resource.(*hcloud.Server) + return server.Labels + }, + GetIDOrName: func(resource any) string { + server := resource.(*hcloud.Server) + return strconv.FormatInt(server.ID, 10) + }, } diff --git a/internal/cmd/server/labels_test.go b/internal/cmd/server/labels_test.go index f1a4746c..634ad9fe 100644 --- a/internal/cmd/server/labels_test.go +++ b/internal/cmd/server/labels_test.go @@ -72,16 +72,18 @@ func TestLabelRemove(t *testing.T) { cmd := server.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + srv := &hcloud.Server{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.ServerClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Server{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(srv, nil, nil) fx.Client.ServerClient.EXPECT(). - Update(gomock.Any(), &hcloud.Server{ID: 123}, hcloud.ServerUpdateOpts{ + Update(gomock.Any(), srv, hcloud.ServerUpdateOpts{ Labels: make(map[string]string), }) @@ -101,18 +103,20 @@ func TestMultiLabelRemove(t *testing.T) { cmd := server.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + srv := &hcloud.Server{ + ID: 123, + Labels: map[string]string{ + "key": "value", + "foo": "bar", + "baz": "qux", + }, + } + 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) + Return(srv, nil, nil) fx.Client.ServerClient.EXPECT(). - Update(gomock.Any(), &hcloud.Server{ID: 123}, hcloud.ServerUpdateOpts{ + Update(gomock.Any(), srv, hcloud.ServerUpdateOpts{ Labels: map[string]string{ "key": "value", }, diff --git a/internal/cmd/sshkey/labels.go b/internal/cmd/sshkey/labels.go index 3bfaf396..f0a62553 100644 --- a/internal/cmd/sshkey/labels.go +++ b/internal/cmd/sshkey/labels.go @@ -2,6 +2,7 @@ package sshkey import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a SSH Key", NameSuggestions: func(c hcapi2.Client) func() []string { return c.SSHKey().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.SSHKey().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { sshKey, _, err := s.Client().SSHKey().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if sshKey == nil { - return nil, 0, fmt.Errorf("ssh key not found: %s", idOrName) + return nil, fmt.Errorf("ssh key not found: %s", idOrName) } - return sshKey.Labels, sshKey.ID, nil + return sshKey, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + sshKey := resource.(*hcloud.SSHKey) opts := hcloud.SSHKeyUpdateOpts{ Labels: labels, } - _, _, err := s.Client().SSHKey().Update(s, &hcloud.SSHKey{ID: id}, opts) + _, _, err := s.Client().SSHKey().Update(s, sshKey, opts) return err }, + GetLabels: func(resource any) map[string]string { + sshKey := resource.(*hcloud.SSHKey) + return sshKey.Labels + }, + GetIDOrName: func(resource any) string { + sshKey := resource.(*hcloud.SSHKey) + return strconv.FormatInt(sshKey.ID, 10) + }, } diff --git a/internal/cmd/sshkey/labels_test.go b/internal/cmd/sshkey/labels_test.go index fdcd4069..01afb189 100644 --- a/internal/cmd/sshkey/labels_test.go +++ b/internal/cmd/sshkey/labels_test.go @@ -45,16 +45,18 @@ func TestLabelRemove(t *testing.T) { cmd := sshkey.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + sshKey := &hcloud.SSHKey{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.SSHKeyClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.SSHKey{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(sshKey, nil, nil) fx.Client.SSHKeyClient.EXPECT(). - Update(gomock.Any(), &hcloud.SSHKey{ID: 123}, hcloud.SSHKeyUpdateOpts{ + Update(gomock.Any(), sshKey, hcloud.SSHKeyUpdateOpts{ Labels: make(map[string]string), }) diff --git a/internal/cmd/volume/labels.go b/internal/cmd/volume/labels.go index 57a8d972..97cdaff4 100644 --- a/internal/cmd/volume/labels.go +++ b/internal/cmd/volume/labels.go @@ -2,6 +2,7 @@ package volume import ( "fmt" + "strconv" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/hcapi2" @@ -15,21 +16,30 @@ var LabelCmds = base.LabelCmds{ ShortDescriptionRemove: "Remove a label from a Volume", NameSuggestions: func(c hcapi2.Client) func() []string { return c.Volume().Names }, LabelKeySuggestions: func(c hcapi2.Client) func(idOrName string) []string { return c.Volume().LabelKeys }, - FetchLabels: func(s state.State, idOrName string) (map[string]string, int64, error) { + Fetch: func(s state.State, idOrName string) (any, error) { volume, _, err := s.Client().Volume().Get(s, idOrName) if err != nil { - return nil, 0, err + return nil, err } if volume == nil { - return nil, 0, fmt.Errorf("volume not found: %s", idOrName) + return nil, fmt.Errorf("volume not found: %s", idOrName) } - return volume.Labels, volume.ID, nil + return volume, nil }, - SetLabels: func(s state.State, id int64, labels map[string]string) error { + SetLabels: func(s state.State, resource any, labels map[string]string) error { + volume := resource.(*hcloud.Volume) opts := hcloud.VolumeUpdateOpts{ Labels: labels, } - _, _, err := s.Client().Volume().Update(s, &hcloud.Volume{ID: id}, opts) + _, _, err := s.Client().Volume().Update(s, volume, opts) return err }, + GetLabels: func(resource any) map[string]string { + volume := resource.(*hcloud.Volume) + return volume.Labels + }, + GetIDOrName: func(resource any) string { + volume := resource.(*hcloud.Volume) + return strconv.FormatInt(volume.ID, 10) + }, } diff --git a/internal/cmd/volume/labels_test.go b/internal/cmd/volume/labels_test.go index 94b80299..2df3c7a4 100644 --- a/internal/cmd/volume/labels_test.go +++ b/internal/cmd/volume/labels_test.go @@ -19,11 +19,13 @@ func TestLabelAdd(t *testing.T) { cmd := volume.LabelCmds.AddCobraCommand(fx.State()) fx.ExpectEnsureToken() + vol := &hcloud.Volume{ID: 123} + fx.Client.VolumeClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Volume{ID: 123}, nil, nil) + Return(vol, nil, nil) fx.Client.VolumeClient.EXPECT(). - Update(gomock.Any(), &hcloud.Volume{ID: 123}, hcloud.VolumeUpdateOpts{ + Update(gomock.Any(), vol, hcloud.VolumeUpdateOpts{ Labels: map[string]string{ "key": "value", }, @@ -45,16 +47,18 @@ func TestLabelRemove(t *testing.T) { cmd := volume.LabelCmds.RemoveCobraCommand(fx.State()) fx.ExpectEnsureToken() + vol := &hcloud.Volume{ + ID: 123, + Labels: map[string]string{ + "key": "value", + }, + } + fx.Client.VolumeClient.EXPECT(). Get(gomock.Any(), "123"). - Return(&hcloud.Volume{ - ID: 123, - Labels: map[string]string{ - "key": "value", - }, - }, nil, nil) + Return(vol, nil, nil) fx.Client.VolumeClient.EXPECT(). - Update(gomock.Any(), &hcloud.Volume{ID: 123}, hcloud.VolumeUpdateOpts{ + Update(gomock.Any(), vol, hcloud.VolumeUpdateOpts{ Labels: make(map[string]string), }) From 09c9f5d4107bd672defcfaaf131bba0581a0796a Mon Sep 17 00:00:00 2001 From: phm07 <22707808+phm07@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:02:20 +0100 Subject: [PATCH 2/2] make base.LabelCmds generic --- internal/cmd/base/labels.go | 18 +++++++++--------- internal/cmd/certificate/labels.go | 13 +++++-------- internal/cmd/firewall/labels.go | 13 +++++-------- internal/cmd/floatingip/labels.go | 13 +++++-------- internal/cmd/image/labels.go | 13 +++++-------- internal/cmd/loadbalancer/labels.go | 13 +++++-------- internal/cmd/network/labels.go | 13 +++++-------- internal/cmd/placementgroup/labels.go | 13 +++++-------- internal/cmd/primaryip/labels.go | 13 +++++-------- internal/cmd/server/labels.go | 13 +++++-------- internal/cmd/sshkey/labels.go | 13 +++++-------- internal/cmd/volume/labels.go | 13 +++++-------- 12 files changed, 64 insertions(+), 97 deletions(-) diff --git a/internal/cmd/base/labels.go b/internal/cmd/base/labels.go index 3b487690..78094e54 100644 --- a/internal/cmd/base/labels.go +++ b/internal/cmd/base/labels.go @@ -14,20 +14,20 @@ import ( ) // LabelCmds allows defining commands for adding labels to resources. -type LabelCmds struct { +type LabelCmds[T any] struct { ResourceNameSingular string ShortDescriptionAdd string ShortDescriptionRemove string NameSuggestions func(client hcapi2.Client) func() []string LabelKeySuggestions func(client hcapi2.Client) func(idOrName string) []string - Fetch func(s state.State, idOrName string) (any, error) - SetLabels func(s state.State, resource any, labels map[string]string) error - GetLabels func(resource any) map[string]string - GetIDOrName func(resource any) string + Fetch func(s state.State, idOrName string) (T, error) + SetLabels func(s state.State, resource T, labels map[string]string) error + GetLabels func(resource T) map[string]string + GetIDOrName func(resource T) string } // AddCobraCommand creates a command that can be registered with cobra. -func (lc *LabelCmds) AddCobraCommand(s state.State) *cobra.Command { +func (lc *LabelCmds[T]) AddCobraCommand(s state.State) *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("add-label [--overwrite] <%s>