diff --git a/internal/cmd/floatingip/create.go b/internal/cmd/floatingip/create.go index 0ef54431..467893ac 100644 --- a/internal/cmd/floatingip/create.go +++ b/internal/cmd/floatingip/create.go @@ -38,6 +38,9 @@ var CreateCommand = base.Cmd{ cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete")) + return cmd }, Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { @@ -56,17 +59,23 @@ var CreateCommand = base.Cmd{ description, _ := cmd.Flags().GetString("description") serverNameOrID, _ := cmd.Flags().GetString("server") labels, _ := cmd.Flags().GetStringToString("label") + protection, _ := cmd.Flags().GetStringSlice("enable-protection") + + protectionOps, err := getChangeProtectionOpts(true, protection) + if err != nil { + return err + } - opts := hcloud.FloatingIPCreateOpts{ + createOpts := hcloud.FloatingIPCreateOpts{ Type: hcloud.FloatingIPType(typ), Description: &description, Labels: labels, } if name != "" { - opts.Name = &name + createOpts.Name = &name } if homeLocation != "" { - opts.HomeLocation = &hcloud.Location{Name: homeLocation} + createOpts.HomeLocation = &hcloud.Location{Name: homeLocation} } if serverNameOrID != "" { server, _, err := client.Server().Get(ctx, serverNameOrID) @@ -76,16 +85,16 @@ var CreateCommand = base.Cmd{ if server == nil { return fmt.Errorf("server not found: %s", serverNameOrID) } - opts.Server = server + createOpts.Server = server } - result, _, err := client.FloatingIP().Create(ctx, opts) + result, _, err := client.FloatingIP().Create(ctx, createOpts) if err != nil { return err } fmt.Printf("Floating IP %d created\n", result.FloatingIP.ID) - return nil + return changeProtection(ctx, client, waiter, result.FloatingIP, true, protectionOps) }, } diff --git a/internal/cmd/loadbalancer/create.go b/internal/cmd/loadbalancer/create.go index a500e041..6753f689 100644 --- a/internal/cmd/loadbalancer/create.go +++ b/internal/cmd/loadbalancer/create.go @@ -42,6 +42,9 @@ var CreateCommand = base.Cmd{ cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete")) + return cmd }, Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { @@ -51,8 +54,14 @@ var CreateCommand = base.Cmd{ location, _ := cmd.Flags().GetString("location") networkZone, _ := cmd.Flags().GetString("network-zone") labels, _ := cmd.Flags().GetStringToString("label") + protection, _ := cmd.Flags().GetStringSlice("enable-protection") + + protectionOpts, err := getChangeProtectionOpts(true, protection) + if err != nil { + return err + } - opts := hcloud.LoadBalancerCreateOpts{ + createOpts := hcloud.LoadBalancerCreateOpts{ Name: name, LoadBalancerType: &hcloud.LoadBalancerType{ Name: serverType, @@ -60,15 +69,15 @@ var CreateCommand = base.Cmd{ Labels: labels, } if algorithmType != "" { - opts.Algorithm = &hcloud.LoadBalancerAlgorithm{Type: hcloud.LoadBalancerAlgorithmType(algorithmType)} + createOpts.Algorithm = &hcloud.LoadBalancerAlgorithm{Type: hcloud.LoadBalancerAlgorithmType(algorithmType)} } if networkZone != "" { - opts.NetworkZone = hcloud.NetworkZone(networkZone) + createOpts.NetworkZone = hcloud.NetworkZone(networkZone) } if location != "" { - opts.Location = &hcloud.Location{Name: location} + createOpts.Location = &hcloud.Location{Name: location} } - result, _, err := client.LoadBalancer().Create(ctx, opts) + result, _, err := client.LoadBalancer().Create(ctx, createOpts) if err != nil { return err } @@ -80,7 +89,12 @@ var CreateCommand = base.Cmd{ if err != nil { return err } - fmt.Printf("LoadBalancer %d created\n", loadBalancer.ID) + fmt.Printf("Load Balancer %d created\n", loadBalancer.ID) + + if err := changeProtection(ctx, client, waiter, loadBalancer, true, protectionOpts); err != nil { + return err + } + 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/network/create.go b/internal/cmd/network/create.go index 432423a3..c466c11a 100644 --- a/internal/cmd/network/create.go +++ b/internal/cmd/network/create.go @@ -1,7 +1,8 @@ package network import ( - "fmt" + "github.com/hetznercloud/cli/internal/cmd/cmpl" + "github.com/hetznercloud/cli/internal/hcapi2" "net" "github.com/hetznercloud/cli/internal/state" @@ -30,6 +31,8 @@ func newCreateCommand(cli *state.State) *cobra.Command { cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete")) return cmd } @@ -38,19 +41,26 @@ func runCreate(cli *state.State, cmd *cobra.Command, args []string) error { ipRange, _ := cmd.Flags().GetIPNet("ip-range") labels, _ := cmd.Flags().GetStringToString("label") exposeRoutesToVSwitch, _ := cmd.Flags().GetBool("expose-routes-to-vswitch") + protection, _ := cmd.Flags().GetStringSlice("enable-protection") - opts := hcloud.NetworkCreateOpts{ + protectionOpts, err := getChangeProtectionOpts(true, protection) + if err != nil { + return err + } + + createOpts := hcloud.NetworkCreateOpts{ Name: name, IPRange: &ipRange, Labels: labels, ExposeRoutesToVSwitch: exposeRoutesToVSwitch, } - network, _, err := cli.Client().Network.Create(cli.Context, opts) + network, _, err := cli.Client().Network.Create(cli.Context, createOpts) if err != nil { return err } - fmt.Printf("Network %d created\n", network.ID) - return nil + cmd.Printf("Network %d created\n", network.ID) + + return changeProtection(cli.Context, hcapi2.NewClient(cli.Client()), cli, network, true, protectionOpts) } diff --git a/internal/cmd/primaryip/create.go b/internal/cmd/primaryip/create.go index 2880cb0e..6ec01534 100644 --- a/internal/cmd/primaryip/create.go +++ b/internal/cmd/primaryip/create.go @@ -3,7 +3,6 @@ package primaryip import ( "context" "fmt" - "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/cmpl" "github.com/hetznercloud/cli/internal/hcapi2" @@ -34,6 +33,10 @@ var CreateCmd = base.Cmd{ cmd.RegisterFlagCompletionFunc("datacenter", cmpl.SuggestCandidatesF(client.Datacenter().Names)) cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete")) + return cmd }, Run: func(ctx context.Context, client hcapi2.Client, actionWaiter state.ActionWaiter, cmd *cobra.Command, args []string) error { @@ -41,24 +44,36 @@ var CreateCmd = base.Cmd{ name, _ := cmd.Flags().GetString("name") assigneeID, _ := cmd.Flags().GetInt64("assignee-id") datacenter, _ := cmd.Flags().GetString("datacenter") + protection, _ := cmd.Flags().GetStringSlice("enable-protection") - opts := hcloud.PrimaryIPCreateOpts{ + protectionOpts, err := getChangeProtectionOpts(true, protection) + if err != nil { + return err + } + + createOpts := hcloud.PrimaryIPCreateOpts{ Type: hcloud.PrimaryIPType(typ), Name: name, AssigneeType: "server", Datacenter: datacenter, } if assigneeID != 0 { - opts.AssigneeID = &assigneeID + createOpts.AssigneeID = &assigneeID } - result, _, err := client.PrimaryIP().Create(ctx, opts) + result, _, err := client.PrimaryIP().Create(ctx, createOpts) if err != nil { return err } fmt.Printf("Primary IP %d created\n", result.PrimaryIP.ID) + if len(protection) > 0 { + if err := changeProtection(ctx, client, actionWaiter, result.PrimaryIP, true, protectionOpts); err != nil { + return err + } + } + return nil }, } diff --git a/internal/cmd/server/create.go b/internal/cmd/server/create.go index 5e4037fd..42082793 100644 --- a/internal/cmd/server/create.go +++ b/internal/cmd/server/create.go @@ -77,16 +77,20 @@ var CreateCmd = base.Cmd{ cmd.Flags().Bool("without-ipv4", false, "Creates the server without an IPv4 (default: false)") cmd.Flags().Bool("without-ipv6", false, "Creates the server without an IPv6 (default: false)") + + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete, rebuild) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete", "rebuild")) + return cmd }, Run: func(ctx context.Context, client hcapi2.Client, actionWaiter state.ActionWaiter, cmd *cobra.Command, args []string) error { - opts, err := createOptsFromFlags(ctx, client, cmd.Flags()) + createOpts, protectionOpts, err := createOptsFromFlags(ctx, client, cmd.Flags()) if err != nil { return err } - result, _, err := client.Server().Create(ctx, opts) + result, _, err := client.Server().Create(ctx, createOpts) if err != nil { return err } @@ -103,6 +107,11 @@ var CreateCmd = base.Cmd{ return err } fmt.Printf("Server %d created\n", result.Server.ID) + + if err := changeProtection(ctx, client, actionWaiter, server, true, protectionOpts); err != nil { + return err + } + if !server.PublicNet.IPv4.IsUnspecified() { fmt.Printf("IPv4: %s\n", server.PublicNet.IPv4.IP.String()) } @@ -196,7 +205,7 @@ func buildUserData(files []string) (string, error) { func createOptsFromFlags( ctx context.Context, client hcapi2.Client, flags *pflag.FlagSet, -) (opts hcloud.ServerCreateOpts, err error) { +) (createOpts hcloud.ServerCreateOpts, protectionOps hcloud.ServerChangeProtectionOpts, err error) { name, _ := flags.GetString("name") serverTypeName, _ := flags.GetString("type") imageIDorName, _ := flags.GetString("image") @@ -216,6 +225,7 @@ func createOptsFromFlags( withoutIPv6, _ := flags.GetBool("without-ipv6") primaryIPv4IDorName, _ := flags.GetString("primary-ipv4") primaryIPv6IDorName, _ := flags.GetString("primary-ipv6") + protection, _ := flags.GetStringSlice("enable-protection") serverType, _, err := client.ServerType().Get(ctx, serverTypeName) if err != nil { @@ -254,7 +264,7 @@ func createOptsFromFlags( err = fmt.Errorf("a server can not be created without IPv4, IPv6 and a private network. Choose at least one of those options to create the server") return } - opts = hcloud.ServerCreateOpts{ + createOpts = hcloud.ServerCreateOpts{ Name: name, ServerType: serverType, Image: image, @@ -293,7 +303,7 @@ func createOptsFromFlags( } publicNetConfiguration.IPv6 = primaryIPv6 } - opts.PublicNet = publicNetConfiguration + createOpts.PublicNet = publicNetConfiguration if len(userDataFiles) == 1 { var data []byte if userDataFiles[0] == "-" { @@ -304,9 +314,9 @@ func createOptsFromFlags( if err != nil { return } - opts.UserData = string(data) + createOpts.UserData = string(data) } else if len(userDataFiles) > 1 { - opts.UserData, err = buildUserData(userDataFiles) + createOpts.UserData, err = buildUserData(userDataFiles) if err != nil { return } @@ -330,7 +340,7 @@ func createOptsFromFlags( err = fmt.Errorf("SSH key not found: %s", sshKeyIDOrName) return } - opts.SSHKeys = append(opts.SSHKeys, sshKey) + createOpts.SSHKeys = append(createOpts.SSHKeys, sshKey) } for _, volumeIDOrName := range volumes { var volume *hcloud.Volume @@ -343,7 +353,7 @@ func createOptsFromFlags( err = fmt.Errorf("volume not found: %s", volumeIDOrName) return } - opts.Volumes = append(opts.Volumes, volume) + createOpts.Volumes = append(createOpts.Volumes, volume) } for _, networkIDOrName := range networks { var network *hcloud.Network @@ -356,7 +366,7 @@ func createOptsFromFlags( err = fmt.Errorf("network not found: %s", networkIDOrName) return } - opts.Networks = append(opts.Networks, network) + createOpts.Networks = append(createOpts.Networks, network) } for _, firewallIDOrName := range firewalls { var firewall *hcloud.Firewall @@ -369,14 +379,14 @@ func createOptsFromFlags( err = fmt.Errorf("firewall not found: %s", firewallIDOrName) return } - opts.Firewalls = append(opts.Firewalls, &hcloud.ServerCreateFirewall{Firewall: *firewall}) + createOpts.Firewalls = append(createOpts.Firewalls, &hcloud.ServerCreateFirewall{Firewall: *firewall}) } if datacenter != "" { - opts.Datacenter = &hcloud.Datacenter{Name: datacenter} + createOpts.Datacenter = &hcloud.Datacenter{Name: datacenter} } if location != "" { - opts.Location = &hcloud.Location{Name: location} + createOpts.Location = &hcloud.Location{Name: location} } if placementGroupIDorName != "" { var placementGroup *hcloud.PlacementGroup @@ -388,9 +398,10 @@ func createOptsFromFlags( err = fmt.Errorf("placement group not found: %s", placementGroupIDorName) return } - opts.PlacementGroup = placementGroup + createOpts.PlacementGroup = placementGroup } + protectionOps, err = getChangeProtectionOpts(true, protection) return } diff --git a/internal/cmd/volume/create.go b/internal/cmd/volume/create.go index d3e17606..69aa6190 100644 --- a/internal/cmd/volume/create.go +++ b/internal/cmd/volume/create.go @@ -41,6 +41,9 @@ var CreateCommand = base.Cmd{ cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)") + cmd.Flags().StringSlice("enable-protection", []string{}, "Enable protection (delete) (default: none)") + cmd.RegisterFlagCompletionFunc("enable-protection", cmpl.SuggestCandidates("delete")) + return cmd }, Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, args []string) error { @@ -51,8 +54,14 @@ var CreateCommand = base.Cmd{ automount, _ := cmd.Flags().GetBool("automount") format, _ := cmd.Flags().GetString("format") labels, _ := cmd.Flags().GetStringToString("label") + protection, _ := cmd.Flags().GetStringSlice("enable-protection") + + protectionOpts, err := getChangeProtectionOpts(true, protection) + if err != nil { + return err + } - opts := hcloud.VolumeCreateOpts{ + createOpts := hcloud.VolumeCreateOpts{ Name: name, Size: size, Labels: labels, @@ -61,9 +70,9 @@ var CreateCommand = base.Cmd{ if location != "" { id, err := strconv.ParseInt(location, 10, 64) if err == nil { - opts.Location = &hcloud.Location{ID: id} + createOpts.Location = &hcloud.Location{ID: id} } else { - opts.Location = &hcloud.Location{Name: location} + createOpts.Location = &hcloud.Location{Name: location} } } if serverIDOrName != "" { @@ -74,16 +83,16 @@ var CreateCommand = base.Cmd{ if server == nil { return fmt.Errorf("server not found: %s", serverIDOrName) } - opts.Server = server + createOpts.Server = server } if automount { - opts.Automount = &automount + createOpts.Automount = &automount } if format != "" { - opts.Format = &format + createOpts.Format = &format } - result, _, err := client.Volume().Create(ctx, opts) + result, _, err := client.Volume().Create(ctx, createOpts) if err != nil { return err } @@ -96,6 +105,6 @@ var CreateCommand = base.Cmd{ } fmt.Printf("Volume %d created\n", result.Volume.ID) - return nil + return changeProtection(ctx, client, waiter, result.Volume, true, protectionOpts) }, }