From 2c5b4a77061ddc95d6656eb8f3da811dcbf9e940 Mon Sep 17 00:00:00 2001 From: samcday Date: Fri, 17 Feb 2023 11:48:29 +0100 Subject: [PATCH] chore: replace hcapi.LocationClient usage with hcapi2 (#446) --- internal/cmd/floatingip/create.go | 131 ++++++++-------- internal/cmd/floatingip/floatingip.go | 2 +- internal/cmd/loadbalancer/create.go | 134 ++++++++--------- internal/cmd/loadbalancer/load_balancer.go | 2 +- internal/cmd/network/add_subnet.go | 116 ++++++++------- internal/cmd/network/network.go | 2 +- internal/cmd/volume/create.go | 140 +++++++++--------- internal/cmd/volume/volume.go | 2 +- internal/hcapi/location.go | 55 ------- internal/hcapi2/location.go | 23 +++ .../hcapi2/mock/zz_location_client_mock.go | 14 ++ internal/state/helpers.go | 16 -- internal/state/state.go | 1 - 13 files changed, 302 insertions(+), 336 deletions(-) delete mode 100644 internal/hcapi/location.go diff --git a/internal/cmd/floatingip/create.go b/internal/cmd/floatingip/create.go index c3ef90f9..b2041a40 100644 --- a/internal/cmd/floatingip/create.go +++ b/internal/cmd/floatingip/create.go @@ -1,96 +1,91 @@ package floatingip import ( + "context" "errors" "fmt" + "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/cmpl" - "github.com/hetznercloud/cli/internal/cmd/util" + "github.com/hetznercloud/cli/internal/hcapi2" "github.com/hetznercloud/cli/internal/state" "github.com/hetznercloud/hcloud-go/hcloud" "github.com/spf13/cobra" ) -func newCreateCommand(cli *state.State) *cobra.Command { - cmd := &cobra.Command{ - Use: "create FLAGS", - Short: "Create a Floating IP", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableFlagsInUseLine: true, - PreRunE: util.ChainRunE(validateCreate, cli.EnsureToken), - RunE: cli.Wrap(runCreate), - } - cmd.Flags().String("type", "", "Type (ipv4 or ipv6) (required)") - cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("ipv4", "ipv6")) - cmd.MarkFlagRequired("type") - - cmd.Flags().String("description", "", "Description") +var CreateCommand = base.Cmd{ + BaseCobraCommand: func(client hcapi2.Client) *cobra.Command { + cmd := &cobra.Command{ + Use: "create FLAGS", + Short: "Create a Floating IP", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableFlagsInUseLine: true, + } + cmd.Flags().String("type", "", "Type (ipv4 or ipv6) (required)") + cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("ipv4", "ipv6")) + cmd.MarkFlagRequired("type") - cmd.Flags().String("name", "", "Name") + cmd.Flags().String("description", "", "Description") - cmd.Flags().String("home-location", "", "Home location") - cmd.RegisterFlagCompletionFunc("home-location", cmpl.SuggestCandidatesF(cli.LocationNames)) + cmd.Flags().String("name", "", "Name") - cmd.Flags().String("server", "", "Server to assign Floating IP to") - cmd.RegisterFlagCompletionFunc("server", cmpl.SuggestCandidatesF(cli.ServerNames)) + cmd.Flags().String("home-location", "", "Home location") + cmd.RegisterFlagCompletionFunc("home-location", cmpl.SuggestCandidatesF(client.Location().Names)) - cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().String("server", "", "Server to assign Floating IP to") + cmd.RegisterFlagCompletionFunc("server", cmpl.SuggestCandidatesF(client.Server().Names)) - return cmd -} + cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") -func validateCreate(cmd *cobra.Command, args []string) error { - typ, _ := cmd.Flags().GetString("type") - if typ == "" { - return errors.New("type is required") - } + return cmd + }, + Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { + typ, _ := cmd.Flags().GetString("type") + if typ == "" { + return errors.New("type is required") + } - homeLocation, _ := cmd.Flags().GetString("home-location") - server, _ := cmd.Flags().GetString("server") - if homeLocation == "" && server == "" { - return errors.New("one of --home-location or --server is required") - } + homeLocation, _ := cmd.Flags().GetString("home-location") + server, _ := cmd.Flags().GetString("server") + if homeLocation == "" && server == "" { + return errors.New("one of --home-location or --server is required") + } - return nil -} + name, _ := cmd.Flags().GetString("name") + description, _ := cmd.Flags().GetString("description") + serverNameOrID, _ := cmd.Flags().GetString("server") + labels, _ := cmd.Flags().GetStringToString("label") -func runCreate(cli *state.State, cmd *cobra.Command, args []string) error { - typ, _ := cmd.Flags().GetString("type") - name, _ := cmd.Flags().GetString("name") - description, _ := cmd.Flags().GetString("description") - homeLocation, _ := cmd.Flags().GetString("home-location") - serverNameOrID, _ := cmd.Flags().GetString("server") - labels, _ := cmd.Flags().GetStringToString("label") + opts := hcloud.FloatingIPCreateOpts{ + Type: hcloud.FloatingIPType(typ), + Description: &description, + Labels: labels, + } + if name != "" { + opts.Name = &name + } + if homeLocation != "" { + opts.HomeLocation = &hcloud.Location{Name: homeLocation} + } + if serverNameOrID != "" { + server, _, err := client.Server().Get(ctx, serverNameOrID) + if err != nil { + return err + } + if server == nil { + return fmt.Errorf("server not found: %s", serverNameOrID) + } + opts.Server = server + } - opts := hcloud.FloatingIPCreateOpts{ - Type: hcloud.FloatingIPType(typ), - Description: &description, - Labels: labels, - } - if name != "" { - opts.Name = &name - } - if homeLocation != "" { - opts.HomeLocation = &hcloud.Location{Name: homeLocation} - } - if serverNameOrID != "" { - server, _, err := cli.Client().Server.Get(cli.Context, serverNameOrID) + result, _, err := client.FloatingIP().Create(ctx, opts) if err != nil { return err } - if server == nil { - return fmt.Errorf("server not found: %s", serverNameOrID) - } - opts.Server = server - } - - result, _, err := cli.Client().FloatingIP.Create(cli.Context, opts) - if err != nil { - return err - } - fmt.Printf("Floating IP %d created\n", result.FloatingIP.ID) + fmt.Printf("Floating IP %d created\n", result.FloatingIP.ID) - return nil + return nil + }, } diff --git a/internal/cmd/floatingip/floatingip.go b/internal/cmd/floatingip/floatingip.go index e99d1309..a0878de6 100644 --- a/internal/cmd/floatingip/floatingip.go +++ b/internal/cmd/floatingip/floatingip.go @@ -17,7 +17,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command { cmd.AddCommand( updateCmd.CobraCommand(cli.Context, client, cli), listCmd.CobraCommand(cli.Context, client, cli), - newCreateCommand(cli), + CreateCommand.CobraCommand(cli.Context, client, cli, cli), describeCmd.CobraCommand(cli.Context, client, cli), newAssignCommand(cli), newUnassignCommand(cli), diff --git a/internal/cmd/loadbalancer/create.go b/internal/cmd/loadbalancer/create.go index 99b8b5ee..73214c11 100644 --- a/internal/cmd/loadbalancer/create.go +++ b/internal/cmd/loadbalancer/create.go @@ -1,86 +1,88 @@ package loadbalancer import ( + "context" "fmt" + "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/cmpl" + "github.com/hetznercloud/cli/internal/hcapi2" "github.com/hetznercloud/cli/internal/state" "github.com/hetznercloud/hcloud-go/hcloud" "github.com/spf13/cobra" ) -func newCreateCommand(cli *state.State) *cobra.Command { - cmd := &cobra.Command{ - Use: "create [FLAGS]", - Short: "Create a Load Balancer", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableFlagsInUseLine: true, - PreRunE: cli.EnsureToken, - RunE: cli.Wrap(runCreate), - } +var CreateCommand = base.Cmd{ + BaseCobraCommand: func(client hcapi2.Client) *cobra.Command { + cmd := &cobra.Command{ + Use: "create [FLAGS]", + Short: "Create a Load Balancer", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableFlagsInUseLine: true, + } - cmd.Flags().String("name", "", "Load Balancer name (required)") - cmd.MarkFlagRequired("name") + cmd.Flags().String("name", "", "Load Balancer name (required)") + cmd.MarkFlagRequired("name") - cmd.Flags().String("type", "", "Load Balancer type (ID or name) (required)") - cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidatesF(cli.LoadBalancerTypeNames)) - cmd.MarkFlagRequired("type") + cmd.Flags().String("type", "", "Load Balancer type (ID or name) (required)") + cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidatesF(client.LoadBalancerType().Names)) + cmd.MarkFlagRequired("type") - cmd.Flags().String("algorithm-type", "", "Algorithm Type name (round_robin or least_connections)") - cmd.RegisterFlagCompletionFunc("algorithm-type", cmpl.SuggestCandidates( - string(hcloud.LoadBalancerAlgorithmTypeLeastConnections), - string(hcloud.LoadBalancerAlgorithmTypeRoundRobin), - )) - cmd.Flags().String("location", "", "Location (ID or name)") - cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(cli.LocationNames)) + cmd.Flags().String("algorithm-type", "", "Algorithm Type name (round_robin or least_connections)") + cmd.RegisterFlagCompletionFunc("algorithm-type", cmpl.SuggestCandidates( + string(hcloud.LoadBalancerAlgorithmTypeLeastConnections), + string(hcloud.LoadBalancerAlgorithmTypeRoundRobin), + )) + cmd.Flags().String("location", "", "Location (ID or name)") + cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(client.Location().Names)) - cmd.Flags().String("network-zone", "", "Network Zone") - cmd.RegisterFlagCompletionFunc("network-zone", cmpl.SuggestCandidatesF(cli.NetworkZoneNames)) + cmd.Flags().String("network-zone", "", "Network Zone") + cmd.RegisterFlagCompletionFunc("network-zone", cmpl.SuggestCandidatesF(client.Location().NetworkZones)) - cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") - return cmd -} - -func runCreate(cli *state.State, cmd *cobra.Command, args []string) error { - name, _ := cmd.Flags().GetString("name") - serverType, _ := cmd.Flags().GetString("type") - algorithmType, _ := cmd.Flags().GetString("algorithm-type") - location, _ := cmd.Flags().GetString("location") - networkZone, _ := cmd.Flags().GetString("network-zone") - labels, _ := cmd.Flags().GetStringToString("label") + return cmd + }, + Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { + name, _ := cmd.Flags().GetString("name") + serverType, _ := cmd.Flags().GetString("type") + algorithmType, _ := cmd.Flags().GetString("algorithm-type") + location, _ := cmd.Flags().GetString("location") + networkZone, _ := cmd.Flags().GetString("network-zone") + labels, _ := cmd.Flags().GetStringToString("label") - opts := hcloud.LoadBalancerCreateOpts{ - Name: name, - LoadBalancerType: &hcloud.LoadBalancerType{ - Name: serverType, - }, - Labels: labels, - } - if algorithmType != "" { - opts.Algorithm = &hcloud.LoadBalancerAlgorithm{Type: hcloud.LoadBalancerAlgorithmType(algorithmType)} - } - if networkZone != "" { - opts.NetworkZone = hcloud.NetworkZone(networkZone) - } - if location != "" { - opts.Location = &hcloud.Location{Name: location} - } - result, _, err := cli.Client().LoadBalancer.Create(cli.Context, opts) - if err != nil { - return err - } + opts := hcloud.LoadBalancerCreateOpts{ + Name: name, + LoadBalancerType: &hcloud.LoadBalancerType{ + Name: serverType, + }, + Labels: labels, + } + if algorithmType != "" { + opts.Algorithm = &hcloud.LoadBalancerAlgorithm{Type: hcloud.LoadBalancerAlgorithmType(algorithmType)} + } + if networkZone != "" { + opts.NetworkZone = hcloud.NetworkZone(networkZone) + } + if location != "" { + opts.Location = &hcloud.Location{Name: location} + } + result, _, err := client.LoadBalancer().Create(ctx, opts) + if err != nil { + return err + } - if err := cli.ActionProgress(cli.Context, result.Action); err != nil { - return err - } - loadBalancer, _, err := cli.Client().LoadBalancer.GetByID(cli.Context, result.LoadBalancer.ID) - if err != nil { - return err - } - fmt.Printf("LoadBalancer %d created\n", loadBalancer.ID) - fmt.Printf("IPv4: %s\n", loadBalancer.PublicNet.IPv4.IP.String()) - fmt.Printf("IPv6: %s\n", loadBalancer.PublicNet.IPv6.IP.String()) - return nil + if err := waiter.ActionProgress(ctx, result.Action); err != nil { + return err + } + loadBalancer, _, err := client.LoadBalancer().GetByID(ctx, result.LoadBalancer.ID) + if err != nil { + return err + } + fmt.Printf("LoadBalancer %d created\n", loadBalancer.ID) + fmt.Printf("IPv4: %s\n", loadBalancer.PublicNet.IPv4.IP.String()) + fmt.Printf("IPv6: %s\n", loadBalancer.PublicNet.IPv6.IP.String()) + return nil + }, } diff --git a/internal/cmd/loadbalancer/load_balancer.go b/internal/cmd/loadbalancer/load_balancer.go index 33a69bd4..eecf7765 100644 --- a/internal/cmd/loadbalancer/load_balancer.go +++ b/internal/cmd/loadbalancer/load_balancer.go @@ -16,7 +16,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command { DisableFlagsInUseLine: true, } cmd.AddCommand( - newCreateCommand(cli), + CreateCommand.CobraCommand(cli.Context, client, cli, cli), ListCmd.CobraCommand(cli.Context, client, cli), DescribeCmd.CobraCommand(cli.Context, client, cli), deleteCmd.CobraCommand(cli.Context, client, cli), diff --git a/internal/cmd/network/add_subnet.go b/internal/cmd/network/add_subnet.go index d02b2714..ce026f7c 100644 --- a/internal/cmd/network/add_subnet.go +++ b/internal/cmd/network/add_subnet.go @@ -1,78 +1,80 @@ package network import ( + "context" "fmt" "net" + "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/cmpl" + "github.com/hetznercloud/cli/internal/hcapi2" "github.com/hetznercloud/cli/internal/state" "github.com/hetznercloud/hcloud-go/hcloud" "github.com/spf13/cobra" ) -func newAddSubnetCommand(cli *state.State) *cobra.Command { - cmd := &cobra.Command{ - Use: "add-subnet NETWORK FLAGS", - Short: "Add a subnet to a network", - Args: cobra.ExactArgs(1), - ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(cli.NetworkNames)), - TraverseChildren: true, - DisableFlagsInUseLine: true, - PreRunE: cli.EnsureToken, - RunE: cli.Wrap(runAddSubnet), - } +var AddSubnetCommand = base.Cmd{ + BaseCobraCommand: func(client hcapi2.Client) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-subnet NETWORK FLAGS", + Short: "Add a subnet to a network", + Args: cobra.ExactArgs(1), + ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Network().Names)), + TraverseChildren: true, + DisableFlagsInUseLine: true, + } - cmd.Flags().String("type", "", "Type of subnet (required)") - cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("cloud", "server", "vswitch")) - cmd.MarkFlagRequired("type") + cmd.Flags().String("type", "", "Type of subnet (required)") + cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("cloud", "server", "vswitch")) + cmd.MarkFlagRequired("type") - cmd.Flags().String("network-zone", "", "Name of network zone (required)") - cmd.RegisterFlagCompletionFunc("network-zone", cmpl.SuggestCandidatesF(cli.NetworkZoneNames)) - cmd.MarkFlagRequired("network-zone") + cmd.Flags().String("network-zone", "", "Name of network zone (required)") + cmd.RegisterFlagCompletionFunc("network-zone", cmpl.SuggestCandidatesF(client.Location().NetworkZones)) + cmd.MarkFlagRequired("network-zone") - cmd.Flags().IPNet("ip-range", net.IPNet{}, "Range to allocate IPs from") + cmd.Flags().IPNet("ip-range", net.IPNet{}, "Range to allocate IPs from") - cmd.Flags().Int("vswitch-id", 0, "ID of the vSwitch") - return cmd -} - -func runAddSubnet(cli *state.State, cmd *cobra.Command, args []string) error { - subnetType, _ := cmd.Flags().GetString("type") - networkZone, _ := cmd.Flags().GetString("network-zone") - ipRange, _ := cmd.Flags().GetIPNet("ip-range") - vSwitchID, _ := cmd.Flags().GetInt("vswitch-id") - idOrName := args[0] + cmd.Flags().Int("vswitch-id", 0, "ID of the vSwitch") + return cmd + }, + Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { + subnetType, _ := cmd.Flags().GetString("type") + networkZone, _ := cmd.Flags().GetString("network-zone") + ipRange, _ := cmd.Flags().GetIPNet("ip-range") + vSwitchID, _ := cmd.Flags().GetInt("vswitch-id") + idOrName := args[0] - network, _, err := cli.Client().Network.Get(cli.Context, idOrName) - if err != nil { - return err - } - if network == nil { - return fmt.Errorf("network not found: %s", idOrName) - } - subnet := hcloud.NetworkSubnet{ - Type: hcloud.NetworkSubnetType(subnetType), - NetworkZone: hcloud.NetworkZone(networkZone), - } + network, _, err := client.Network().Get(ctx, idOrName) + if err != nil { + return err + } + if network == nil { + return fmt.Errorf("network not found: %s", idOrName) + } + subnet := hcloud.NetworkSubnet{ + Type: hcloud.NetworkSubnetType(subnetType), + NetworkZone: hcloud.NetworkZone(networkZone), + } - if ipRange.IP != nil && ipRange.Mask != nil { - subnet.IPRange = &ipRange - } - if subnetType == "vswitch" { - subnet.VSwitchID = vSwitchID - } + if ipRange.IP != nil && ipRange.Mask != nil { + subnet.IPRange = &ipRange + } + if subnetType == "vswitch" { + subnet.VSwitchID = vSwitchID + } - opts := hcloud.NetworkAddSubnetOpts{ - Subnet: subnet, - } - action, _, err := cli.Client().Network.AddSubnet(cli.Context, network, opts) - if err != nil { - return err - } - if err := cli.ActionProgress(cli.Context, action); err != nil { - return err - } - fmt.Printf("Subnet added to network %d\n", network.ID) + opts := hcloud.NetworkAddSubnetOpts{ + Subnet: subnet, + } + action, _, err := client.Network().AddSubnet(ctx, network, opts) + if err != nil { + return err + } + if err := waiter.ActionProgress(ctx, action); err != nil { + return err + } + fmt.Printf("Subnet added to network %d\n", network.ID) - return nil + return nil + }, } diff --git a/internal/cmd/network/network.go b/internal/cmd/network/network.go index 355a2dff..6bd24127 100644 --- a/internal/cmd/network/network.go +++ b/internal/cmd/network/network.go @@ -23,7 +23,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command { newChangeIPRangeCommand(cli), newAddRouteCommand(cli), newRemoveRouteCommand(cli), - newAddSubnetCommand(cli), + AddSubnetCommand.CobraCommand(cli.Context, client, cli, cli), newRemoveSubnetCommand(cli), labelCmds.AddCobraCommand(cli.Context, client, cli), labelCmds.RemoveCobraCommand(cli.Context, client, cli), diff --git a/internal/cmd/volume/create.go b/internal/cmd/volume/create.go index 1095e09e..317274c4 100644 --- a/internal/cmd/volume/create.go +++ b/internal/cmd/volume/create.go @@ -1,97 +1,99 @@ package volume import ( + "context" "fmt" "strconv" + "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/cmpl" + "github.com/hetznercloud/cli/internal/hcapi2" "github.com/hetznercloud/cli/internal/state" "github.com/hetznercloud/hcloud-go/hcloud" "github.com/spf13/cobra" ) -func newCreateCommand(cli *state.State) *cobra.Command { - cmd := &cobra.Command{ - Use: "create FLAGS", - Short: "Create a volume", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableFlagsInUseLine: true, - PreRunE: cli.EnsureToken, - RunE: cli.Wrap(runCreate), - } - cmd.Flags().String("name", "", "Volume name (required)") - cmd.MarkFlagRequired("name") - - cmd.Flags().String("server", "", "Server (ID or name)") - cmd.RegisterFlagCompletionFunc("server", cmpl.SuggestCandidatesF(cli.ServerNames)) +var CreateCommand = base.Cmd{ + BaseCobraCommand: func(client hcapi2.Client) *cobra.Command { + cmd := &cobra.Command{ + Use: "create FLAGS", + Short: "Create a volume", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableFlagsInUseLine: true, + } + cmd.Flags().String("name", "", "Volume name (required)") + cmd.MarkFlagRequired("name") - cmd.Flags().String("location", "", "Location (ID or name)") - cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(cli.LocationNames)) + cmd.Flags().String("server", "", "Server (ID or name)") + cmd.RegisterFlagCompletionFunc("server", cmpl.SuggestCandidatesF(client.Server().Names)) - cmd.Flags().Int("size", 0, "Size (GB) (required)") - cmd.MarkFlagRequired("size") + cmd.Flags().String("location", "", "Location (ID or name)") + cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(client.Location().Names)) - cmd.Flags().Bool("automount", false, "Automount volume after attach (server must be provided)") - cmd.Flags().String("format", "", "Format volume after creation (automount must be enabled)") + cmd.Flags().Int("size", 0, "Size (GB) (required)") + cmd.MarkFlagRequired("size") - cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().Bool("automount", false, "Automount volume after attach (server must be provided)") + cmd.Flags().String("format", "", "Format volume after creation (automount must be enabled)") - return cmd -} + cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") -func runCreate(cli *state.State, cmd *cobra.Command, args []string) error { - name, _ := cmd.Flags().GetString("name") - serverIDOrName, _ := cmd.Flags().GetString("server") - size, _ := cmd.Flags().GetInt("size") - location, _ := cmd.Flags().GetString("location") - automount, _ := cmd.Flags().GetBool("automount") - format, _ := cmd.Flags().GetString("format") - labels, _ := cmd.Flags().GetStringToString("label") + return cmd + }, + Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { + name, _ := cmd.Flags().GetString("name") + serverIDOrName, _ := cmd.Flags().GetString("server") + size, _ := cmd.Flags().GetInt("size") + location, _ := cmd.Flags().GetString("location") + automount, _ := cmd.Flags().GetBool("automount") + format, _ := cmd.Flags().GetString("format") + labels, _ := cmd.Flags().GetStringToString("label") - opts := hcloud.VolumeCreateOpts{ - Name: name, - Size: size, - Labels: labels, - } + opts := hcloud.VolumeCreateOpts{ + Name: name, + Size: size, + Labels: labels, + } - if location != "" { - id, err := strconv.Atoi(location) - if err == nil { - opts.Location = &hcloud.Location{ID: id} - } else { - opts.Location = &hcloud.Location{Name: location} + if location != "" { + id, err := strconv.Atoi(location) + if err == nil { + opts.Location = &hcloud.Location{ID: id} + } else { + opts.Location = &hcloud.Location{Name: location} + } + } + if serverIDOrName != "" { + server, _, err := client.Server().Get(ctx, serverIDOrName) + if err != nil { + return err + } + if server == nil { + return fmt.Errorf("server not found: %s", serverIDOrName) + } + opts.Server = server + } + if automount { + opts.Automount = &automount + if format != "" { + opts.Format = &format + } } - } - if serverIDOrName != "" { - server, _, err := cli.Client().Server.Get(cli.Context, serverIDOrName) + + result, _, err := client.Volume().Create(ctx, opts) if err != nil { return err } - if server == nil { - return fmt.Errorf("server not found: %s", serverIDOrName) + + if err := waiter.ActionProgress(ctx, result.Action); err != nil { + return err } - opts.Server = server - } - if automount { - opts.Automount = &automount - if format != "" { - opts.Format = &format + if err := waiter.WaitForActions(ctx, result.NextActions); err != nil { + return err } - } - - result, _, err := cli.Client().Volume.Create(cli.Context, opts) - if err != nil { - return err - } - - if err := cli.ActionProgress(cli.Context, result.Action); err != nil { - return err - } - if err := cli.WaitForActions(cli.Context, result.NextActions); err != nil { - return err - } - fmt.Printf("Volume %d created\n", result.Volume.ID) + fmt.Printf("Volume %d created\n", result.Volume.ID) - return nil + return nil + }, } diff --git a/internal/cmd/volume/volume.go b/internal/cmd/volume/volume.go index f2948cce..7258a538 100644 --- a/internal/cmd/volume/volume.go +++ b/internal/cmd/volume/volume.go @@ -16,7 +16,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command { } cmd.AddCommand( listCmd.CobraCommand(cli.Context, client, cli), - newCreateCommand(cli), + CreateCommand.CobraCommand(cli.Context, client, cli, cli), updateCmd.CobraCommand(cli.Context, client, cli), deleteCmd.CobraCommand(cli.Context, client, cli), describeCmd.CobraCommand(cli.Context, client, cli), diff --git a/internal/hcapi/location.go b/internal/hcapi/location.go deleted file mode 100644 index aab63cdb..00000000 --- a/internal/hcapi/location.go +++ /dev/null @@ -1,55 +0,0 @@ -package hcapi - -import ( - "context" - "strconv" - - "github.com/hetznercloud/hcloud-go/hcloud" -) - -// LocationClient embeds the Hetzner Cloud Location client and provides some -// additional helper functions. -type LocationClient struct { - *hcloud.LocationClient -} - -// LocationNames obtains a list of available locations. It returns nil if -// location names could not be fetched. -func (c *LocationClient) LocationNames() []string { - locs, err := c.All(context.Background()) - if err != nil || len(locs) == 0 { - return nil - } - names := make([]string, len(locs)) - for i, loc := range locs { - name := loc.Name - if name == "" { - name = strconv.Itoa(loc.ID) - } - names[i] = name - } - return names -} - -// NetworkZoneNames obtains a list of available network zones. It returns nil if -// network zone names could not be fetched. -func (c *LocationClient) NetworkZoneNames() []string { - locs, err := c.All(context.Background()) - if err != nil || len(locs) == 0 { - return nil - } - // Use map to get unique elements - namesMap := map[hcloud.NetworkZone]bool{} - for _, loc := range locs { - name := loc.NetworkZone - namesMap[name] = true - } - - // Unique names from map to slice - names := make([]string, len(namesMap)) - for name := range namesMap { - names = append(names, string(name)) - } - - return names -} diff --git a/internal/hcapi2/location.go b/internal/hcapi2/location.go index 17aabf0a..04ed364c 100644 --- a/internal/hcapi2/location.go +++ b/internal/hcapi2/location.go @@ -10,6 +10,7 @@ import ( type LocationClient interface { LocationClientBase Names() []string + NetworkZones() []string } func NewLocationClient(client LocationClientBase) LocationClient { @@ -39,3 +40,25 @@ func (c *locationClient) Names() []string { } return names } + +// NetworkZones obtains a list of available network zones. It returns nil if +// location data could not be fetched. +func (c *locationClient) NetworkZones() []string { + locs, err := c.All(context.Background()) + if err != nil || len(locs) == 0 { + return nil + } + + zones := make(map[string]bool) + for _, loc := range locs { + if loc.NetworkZone != "" { + zones[string(loc.NetworkZone)] = true + } + } + + var zoneList []string + for zone := range zones { + zoneList = append(zoneList, zone) + } + return zoneList +} diff --git a/internal/hcapi2/mock/zz_location_client_mock.go b/internal/hcapi2/mock/zz_location_client_mock.go index 7956dac5..a434bab1 100644 --- a/internal/hcapi2/mock/zz_location_client_mock.go +++ b/internal/hcapi2/mock/zz_location_client_mock.go @@ -127,3 +127,17 @@ func (mr *MockLocationClientMockRecorder) Names() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Names", reflect.TypeOf((*MockLocationClient)(nil).Names)) } + +// NetworkZones mocks base method. +func (m *MockLocationClient) NetworkZones() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetworkZones") + ret0, _ := ret[0].([]string) + return ret0 +} + +// NetworkZones indicates an expected call of NetworkZones. +func (mr *MockLocationClientMockRecorder) NetworkZones() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkZones", reflect.TypeOf((*MockLocationClient)(nil).NetworkZones)) +} diff --git a/internal/state/helpers.go b/internal/state/helpers.go index 645ad576..28b64c90 100644 --- a/internal/state/helpers.go +++ b/internal/state/helpers.go @@ -87,22 +87,6 @@ func (c *State) FloatingIPLabelKeys(idOrName string) []string { return c.floatingIPClient.FloatingIPLabelKeys(idOrName) } -func (c *State) LocationNames() []string { - if c.locationClient == nil { - client := c.Client() - c.locationClient = &hcapi.LocationClient{LocationClient: &client.Location} - } - return c.locationClient.LocationNames() -} - -func (c *State) NetworkZoneNames() []string { - if c.locationClient == nil { - client := c.Client() - c.locationClient = &hcapi.LocationClient{LocationClient: &client.Location} - } - return c.locationClient.NetworkZoneNames() -} - func (c *State) SSHKeyNames() []string { if c.sshKeyClient == nil { client := c.Client() diff --git a/internal/state/state.go b/internal/state/state.go index 01cc6430..8dcef2ea 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -22,7 +22,6 @@ type State struct { DebugFilePath string client *hcloud.Client - locationClient *hcapi.LocationClient sshKeyClient *hcapi.SSHKeyClient volumeClient *hcapi.VolumeClient floatingIPClient *hcapi.FloatingIPClient