server

package
v1.50.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2025 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AddToPlacementGroupCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:               "add-to-placement-group --placement-group <placement-group> <server>",
			Short:             "Add a server to a placement group",
			ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
		}

		cmd.Flags().StringP("placement-group", "g", "", "Placement Group (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("placement-group", cmpl.SuggestCandidatesF(client.PlacementGroup().Names))
		_ = cmd.MarkFlagRequired(("placement-group"))

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found %s", idOrName)
		}

		placementGroupIDOrName, _ := cmd.Flags().GetString("placement-group")
		placementGroup, _, err := s.Client().PlacementGroup().Get(s, placementGroupIDOrName)
		if err != nil {
			return err
		}
		if placementGroup == nil {
			return fmt.Errorf("placement group not found %s", placementGroupIDOrName)
		}

		action, _, err := s.Client().Server().AddToPlacementGroup(s, server, placementGroup)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d added to placement group %s\n", server.ID, placementGroupIDOrName)
		return nil
	},
}
View Source
var AttachISOCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:              "attach-iso <server> <iso>",
			Short:            "Attach an ISO to a server",
			TraverseChildren: true,
			ValidArgsFunction: cmpl.SuggestArgs(
				cmpl.SuggestCandidatesF(client.Server().Names),
				cmpl.SuggestCandidatesF(client.ISO().Names),
			),
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		isoIDOrName := args[1]
		iso, _, err := s.Client().ISO().Get(s, isoIDOrName)
		if err != nil {
			return err
		}
		if iso == nil {
			return fmt.Errorf("ISO not found: %s", isoIDOrName)
		}

		if iso.Architecture != nil && *iso.Architecture != server.ServerType.Architecture {
			return errors.New("failed to attach iso: iso has a different architecture than the server")
		}

		action, _, err := s.Client().Server().AttachISO(s, server, iso)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("ISO %s attached to server %d\n", iso.Name, server.ID)
		return nil
	},
}
View Source
var AttachToNetworkCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "attach-to-network [options] --network <network> <server>",
			Short:                 "Attach a server to a network",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().StringP("network", "n", "", "Network (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("network", cmpl.SuggestCandidatesF(client.Network().Names))
		_ = cmd.MarkFlagRequired("network")

		cmd.Flags().IP("ip", nil, "IP address to assign to the server (auto-assigned if omitted)")
		cmd.Flags().IPSlice("alias-ips", []net.IP{}, "Additional IP addresses to be assigned to the server")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		networkIDOrName, _ := cmd.Flags().GetString("network")
		network, _, err := s.Client().Network().Get(s, networkIDOrName)
		if err != nil {
			return err
		}
		if network == nil {
			return fmt.Errorf("network not found: %s", networkIDOrName)
		}

		ip, _ := cmd.Flags().GetIP("ip")
		aliasIPs, _ := cmd.Flags().GetIPSlice("alias-ips")

		opts := hcloud.ServerAttachToNetworkOpts{
			Network: network,
			IP:      ip,
		}
		opts.AliasIPs = append(opts.AliasIPs, aliasIPs...)
		action, _, err := s.Client().Server().AttachToNetwork(s, server, opts)

		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d attached to network %d\n", server.ID, network.ID)
		return nil
	},
}
View Source
var ChangeAliasIPsCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "change-alias-ips [options] --network <network> <server>",
			Short:                 "Change a server's alias IPs in a network",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().StringP("network", "n", "", "Network (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("network", cmpl.SuggestCandidatesF(client.Network().Names))
		_ = cmd.MarkFlagRequired("network")

		cmd.Flags().StringSlice("alias-ips", nil, "New alias IPs")
		cmd.Flags().Bool("clear", false, "Remove all alias IPs")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		clearAll, _ := cmd.Flags().GetBool("clear")
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		networkIDOrName, _ := cmd.Flags().GetString("network")
		network, _, err := s.Client().Network().Get(s, networkIDOrName)
		if err != nil {
			return err
		}
		if network == nil {
			return fmt.Errorf("network not found: %s", networkIDOrName)
		}

		aliasIPs, _ := cmd.Flags().GetStringSlice("alias-ips")

		opts := hcloud.ServerChangeAliasIPsOpts{
			Network: network,
		}
		if clearAll {
			opts.AliasIPs = []net.IP{}
		} else {
			for _, aliasIP := range aliasIPs {
				opts.AliasIPs = append(opts.AliasIPs, net.ParseIP(aliasIP))
			}
		}
		action, _, err := s.Client().Server().ChangeAliasIPs(s, server, opts)

		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Alias IPs changed for server %d in network %d\n", server.ID, network.ID)
		return nil
	},
}
View Source
var ChangeTypeCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:   "change-type [--keep-disk] <server> <server-type>",
			Short: "Change type of a server",
			ValidArgsFunction: cmpl.SuggestArgs(
				cmpl.SuggestCandidatesF(client.Server().Names),
				cmpl.SuggestCandidatesF(client.ServerType().Names),
			),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().Bool("keep-disk", false, "Keep disk size of current server type. This enables downgrading the server.")
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		serverTypeIDOrName := args[1]
		serverType, _, err := s.Client().ServerType().Get(s, serverTypeIDOrName)
		if err != nil {
			return err
		}
		if serverType == nil {
			return fmt.Errorf("server type not found: %s", serverTypeIDOrName)
		}

		if serverType.IsDeprecated() {
			cmd.Print(warningDeprecatedServerType(serverType))
		}

		keepDisk, _ := cmd.Flags().GetBool("keep-disk")
		opts := hcloud.ServerChangeTypeOpts{
			ServerType:  serverType,
			UpgradeDisk: !keepDisk,
		}
		action, _, err := s.Client().Server().ChangeType(s, server, opts)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		if opts.UpgradeDisk {
			cmd.Printf("Server %d changed to type %s\n", server.ID, serverType.Name)
		} else {
			cmd.Printf("Server %d changed to type %s (disk size was unchanged)\n", server.ID, serverType.Name)
		}
		return nil
	},
}
View Source
var CreateCmd = base.CreateCmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:   "create [options] --name <name> --type <server-type> --image <image>",
			Short: "Create a server",
		}

		cmd.Flags().String("name", "", "Server name (required)")
		_ = cmd.MarkFlagRequired("name")

		cmd.Flags().String("type", "", "Server type (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidatesF(client.ServerType().Names))
		_ = cmd.MarkFlagRequired("type")

		cmd.Flags().String("image", "", "Image (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("image", cmpl.SuggestCandidatesF(client.Image().Names))
		_ = cmd.MarkFlagRequired("image")

		cmd.Flags().String("location", "", "Location (ID or name)")
		_ = cmd.RegisterFlagCompletionFunc("location", cmpl.SuggestCandidatesF(client.Location().Names))

		cmd.Flags().String("datacenter", "", "Datacenter (ID or name)")
		_ = cmd.RegisterFlagCompletionFunc("datacenter", cmpl.SuggestCandidatesF(client.Datacenter().Names))

		cmd.Flags().StringSlice("ssh-key", nil, "ID or name of SSH key to inject (can be specified multiple times)")
		_ = cmd.RegisterFlagCompletionFunc("ssh-key", cmpl.SuggestCandidatesF(client.SSHKey().Names))

		cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)")

		cmd.Flags().StringArray("user-data-from-file", []string{}, "Read user data from specified file (use - to read from stdin)")

		cmd.Flags().Bool("start-after-create", true, "Start server right after creation")

		cmd.Flags().StringSlice("volume", nil, "ID or name of volume to attach (can be specified multiple times)")
		_ = cmd.RegisterFlagCompletionFunc("volume", cmpl.SuggestCandidatesF(client.Volume().Names))

		cmd.Flags().StringSlice("network", nil, "ID or name of network to attach the server to (can be specified multiple times)")
		_ = cmd.RegisterFlagCompletionFunc("network", cmpl.SuggestCandidatesF(client.Network().Names))

		cmd.Flags().StringSlice("firewall", nil, "ID or name of Firewall to attach the server to (can be specified multiple times)")
		_ = cmd.RegisterFlagCompletionFunc("firewall", cmpl.SuggestCandidatesF(client.Firewall().Names))

		cmd.Flags().Bool("automount", false, "Automount volumes after attach (default: false)")
		cmd.Flags().Bool("allow-deprecated-image", false, "Enable the use of deprecated images (default: false)")

		cmd.Flags().String("placement-group", "", "Placement Group (ID of name)")
		_ = cmd.RegisterFlagCompletionFunc("placement-group", cmpl.SuggestCandidatesF(client.PlacementGroup().Names))
		cmd.Flags().String("primary-ipv4", "", "Primary IPv4 (ID of name)")
		_ = cmd.RegisterFlagCompletionFunc("primary-ipv4", cmpl.SuggestCandidatesF(client.PrimaryIP().IPv4Names))
		cmd.Flags().String("primary-ipv6", "", "Primary IPv6 (ID of name)")
		_ = cmd.RegisterFlagCompletionFunc("primary-ipv6", cmpl.SuggestCandidatesF(client.PrimaryIP().IPv6Names))

		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"))

		cmd.Flags().Bool("enable-backup", false, "Enable automatic backups")

		return cmd
	},

	Run: func(s state.State, cmd *cobra.Command, _ []string) (any, any, error) {
		createOpts, protectionOpts, err := createOptsFromFlags(s, cmd)
		if err != nil {
			return nil, nil, err
		}

		result, _, err := s.Client().Server().Create(s, createOpts)
		if err != nil {
			return nil, nil, err
		}

		if err := s.WaitForActions(s, cmd, actionutil.AppendNext(result.Action, result.NextActions)...); err != nil {
			return nil, nil, err
		}

		server, _, err := s.Client().Server().GetByID(s, result.Server.ID)
		if err != nil {
			return nil, nil, err
		}

		cmd.Printf("Server %d created\n", result.Server.ID)

		if err := changeProtection(s, cmd, server, true, protectionOpts); err != nil {
			return nil, nil, err
		}

		enableBackup, _ := cmd.Flags().GetBool("enable-backup")
		if enableBackup {
			action, _, err := s.Client().Server().EnableBackup(s, server, "")
			if err != nil {
				return nil, nil, err
			}

			if err := s.WaitForActions(s, cmd, action); err != nil {
				return nil, nil, err
			}

			cmd.Printf("Backups enabled for server %d\n", server.ID)
		}

		return createResult{Server: server, RootPassword: result.RootPassword},
			createResultSchema{Server: hcloud.SchemaFromServer(server), RootPassword: result.RootPassword}, nil
	},

	PrintResource: func(s state.State, cmd *cobra.Command, resource any) {
		result := resource.(createResult)
		server := result.Server

		if !server.PublicNet.IPv4.IsUnspecified() {
			cmd.Printf("IPv4: %s\n", server.PublicNet.IPv4.IP.String())
		}
		if !server.PublicNet.IPv6.IsUnspecified() {
			cmd.Printf("IPv6: %s1\n", server.PublicNet.IPv6.Network.IP.String())
			cmd.Printf("IPv6 Network: %s\n", server.PublicNet.IPv6.Network.String())
		}
		if len(server.PrivateNet) > 0 {
			cmd.Printf("Private Networks:\n")
			for _, network := range server.PrivateNet {
				cmd.Printf("\t- %s (%s)\n", network.IP.String(), s.Client().Network().Name(network.Network.ID))
			}
		}

		if result.RootPassword != "" {
			cmd.Printf("Root password: %s\n", result.RootPassword)
		}
	},
}

CreateCmd defines a command for creating a server.

View Source
var CreateImageCmd = base.Cmd{
	BaseCobraCommand: func(hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:   "create-image [options] --type <snapshot|backup> <server>",
			Short: "Create an image from a server",
		}
		cmd.Flags().String("type", "", "Image type (required)")
		_ = cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("backup", "snapshot"))
		_ = cmd.MarkFlagRequired("type")

		cmd.Flags().String("description", "", "Image description")

		cmd.Flags().StringToString("label", nil, "User-defined labels ('key=value') (can be specified multiple times)")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		imageType, _ := cmd.Flags().GetString("type")
		description, _ := cmd.Flags().GetString("description")
		labels, _ := cmd.Flags().GetStringToString("label")

		switch hcloud.ImageType(imageType) {
		case hcloud.ImageTypeBackup, hcloud.ImageTypeSnapshot:
			break
		default:
			return fmt.Errorf("invalid image type: %v", imageType)
		}

		opts := &hcloud.ServerCreateImageOpts{
			Type:        hcloud.ImageType(imageType),
			Description: hcloud.Ptr(description),
			Labels:      labels,
		}
		result, _, err := s.Client().Server().CreateImage(s, server, opts)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, result.Action); err != nil {
			return err
		}

		cmd.Printf("Image %d created from server %d\n", result.Image.ID, server.ID)

		return nil
	},
}
View Source
var DeleteCmd = base.DeleteCmd{
	ResourceNameSingular: "Server",
	ResourceNamePlural:   "Servers",
	ShortDescription:     "Delete a server",
	NameSuggestions:      func(c hcapi2.Client) func() []string { return c.Server().Names },
	Fetch: func(s state.State, _ *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
		return s.Client().Server().Get(s, idOrName)
	},
	Delete: func(s state.State, _ *cobra.Command, resource interface{}) (*hcloud.Action, error) {
		server := resource.(*hcloud.Server)
		result, _, err := s.Client().Server().DeleteWithResult(s, server)
		if err != nil {
			return nil, err
		}
		return result.Action, nil
	},
}
View Source
var DescribeCmd = base.DescribeCmd{
	ResourceNameSingular: "server",
	ShortDescription:     "Describe a server",
	JSONKeyGetByID:       "server",
	JSONKeyGetByName:     "servers",
	NameSuggestions:      func(c hcapi2.Client) func() []string { return c.Server().Names },
	Fetch: func(s state.State, _ *cobra.Command, idOrName string) (interface{}, interface{}, error) {
		srv, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return nil, nil, err
		}
		return srv, hcloud.SchemaFromServer(srv), nil
	},
	PrintText: func(s state.State, cmd *cobra.Command, resource interface{}) error {
		server := resource.(*hcloud.Server)

		cmd.Printf("ID:\t\t%d\n", server.ID)
		cmd.Printf("Name:\t\t%s\n", server.Name)
		cmd.Printf("Status:\t\t%s\n", server.Status)
		cmd.Printf("Created:\t%s (%s)\n", util.Datetime(server.Created), humanize.Time(server.Created))

		cmd.Printf("Server Type:\t%s (ID: %d)\n", server.ServerType.Name, server.ServerType.ID)
		cmd.Printf("  ID:\t\t%d\n", server.ServerType.ID)
		cmd.Printf("  Name:\t\t%s\n", server.ServerType.Name)
		cmd.Printf("  Description:\t%s\n", server.ServerType.Description)
		cmd.Printf("  Cores:\t%d\n", server.ServerType.Cores)
		cmd.Printf("  CPU Type:\t%s\n", server.ServerType.CPUType)
		cmd.Printf("  Memory:\t%v GB\n", server.ServerType.Memory)
		cmd.Printf("  Disk:\t\t%d GB\n", server.PrimaryDiskSize)
		cmd.Printf("  Storage Type:\t%s\n", server.ServerType.StorageType)
		cmd.Print(util.PrefixLines(util.DescribeDeprecation(server.ServerType), "  "))

		cmd.Printf("Public Net:\n")
		cmd.Printf("  IPv4:\n")
		if server.PublicNet.IPv4.IsUnspecified() {
			cmd.Printf("    No Primary IPv4\n")
		} else {
			cmd.Printf("    ID:\t\t%d\n", server.PublicNet.IPv4.ID)
			cmd.Printf("    IP:\t\t%s\n", server.PublicNet.IPv4.IP)
			cmd.Printf("    Blocked:\t%s\n", util.YesNo(server.PublicNet.IPv4.Blocked))
			cmd.Printf("    DNS:\t%s\n", server.PublicNet.IPv4.DNSPtr)
		}

		cmd.Printf("  IPv6:\n")
		if server.PublicNet.IPv6.IsUnspecified() {
			cmd.Printf("    No Primary IPv6\n")
		} else {
			cmd.Printf("    ID:\t\t%d\n", server.PublicNet.IPv6.ID)
			cmd.Printf("    IP:\t\t%s\n", server.PublicNet.IPv6.Network.String())
			cmd.Printf("    Blocked:\t%s\n", util.YesNo(server.PublicNet.IPv6.Blocked))
		}
		cmd.Printf("  Floating IPs:\n")
		if len(server.PublicNet.FloatingIPs) > 0 {
			for _, f := range server.PublicNet.FloatingIPs {
				floatingIP, _, err := s.Client().FloatingIP().GetByID(s, f.ID)
				if err != nil {
					return fmt.Errorf("error fetching Floating IP: %w", err)
				}
				cmd.Printf("  - ID:\t\t\t%d\n", floatingIP.ID)
				cmd.Printf("    Description:\t%s\n", util.NA(floatingIP.Description))
				cmd.Printf("    IP:\t\t\t%s\n", floatingIP.IP)
			}
		} else {
			cmd.Printf("    No Floating IPs\n")
		}

		cmd.Printf("Private Net:\n")
		if len(server.PrivateNet) > 0 {
			for _, n := range server.PrivateNet {
				network, _, err := s.Client().Network().GetByID(s, n.Network.ID)
				if err != nil {
					return fmt.Errorf("error fetching network: %w", err)
				}
				cmd.Printf("  - ID:\t\t\t%d\n", network.ID)
				cmd.Printf("    Name:\t\t%s\n", network.Name)
				cmd.Printf("    IP:\t\t\t%s\n", n.IP.String())
				cmd.Printf("    MAC Address:\t%s\n", n.MACAddress)
				if len(n.Aliases) > 0 {
					cmd.Printf("    Alias IPs:\n")
					for _, a := range n.Aliases {
						cmd.Printf("     -\t\t\t%s\n", a)
					}
				} else {
					cmd.Printf("    Alias IPs:\t\t%s\n", util.NA(""))
				}
			}
		} else {
			cmd.Printf("    No Private Networks\n")
		}

		cmd.Printf("Volumes:\n")
		if len(server.Volumes) > 0 {
			for _, v := range server.Volumes {
				volume, _, err := s.Client().Volume().GetByID(s, v.ID)
				if err != nil {
					return fmt.Errorf("error fetching Volume: %w", err)
				}
				cmd.Printf("  - ID:\t\t%d\n", volume.ID)
				cmd.Printf("    Name:\t%s\n", volume.Name)
				cmd.Printf("    Size:\t%s\n", humanize.Bytes(uint64(volume.Size)*humanize.GByte))
			}
		} else {
			cmd.Printf("  No Volumes\n")
		}
		cmd.Printf("Image:\n")
		if server.Image != nil {
			image := server.Image
			cmd.Printf("  ID:\t\t%d\n", image.ID)
			cmd.Printf("  Type:\t\t%s\n", image.Type)
			cmd.Printf("  Status:\t%s\n", image.Status)
			cmd.Printf("  Name:\t\t%s\n", util.NA(image.Name))
			cmd.Printf("  Description:\t%s\n", image.Description)
			if image.ImageSize != 0 {
				cmd.Printf("  Image size:\t%.2f GB\n", image.ImageSize)
			} else {
				cmd.Printf("  Image size:\t%s\n", util.NA(""))
			}
			cmd.Printf("  Disk size:\t%.0f GB\n", image.DiskSize)
			cmd.Printf("  Created:\t%s (%s)\n", util.Datetime(image.Created), humanize.Time(image.Created))
			cmd.Printf("  OS flavor:\t%s\n", image.OSFlavor)
			cmd.Printf("  OS version:\t%s\n", util.NA(image.OSVersion))
			cmd.Printf("  Rapid deploy:\t%s\n", util.YesNo(image.RapidDeploy))
		} else {
			cmd.Printf("  No Image\n")
		}

		cmd.Printf("Datacenter:\n")
		cmd.Printf("  ID:\t\t%d\n", server.Datacenter.ID)
		cmd.Printf("  Name:\t\t%s\n", server.Datacenter.Name)
		cmd.Printf("  Description:\t%s\n", server.Datacenter.Description)
		cmd.Printf("  Location:\n")
		cmd.Printf("    Name:\t\t%s\n", server.Datacenter.Location.Name)
		cmd.Printf("    Description:\t%s\n", server.Datacenter.Location.Description)
		cmd.Printf("    Country:\t\t%s\n", server.Datacenter.Location.Country)
		cmd.Printf("    City:\t\t%s\n", server.Datacenter.Location.City)
		cmd.Printf("    Latitude:\t\t%f\n", server.Datacenter.Location.Latitude)
		cmd.Printf("    Longitude:\t\t%f\n", server.Datacenter.Location.Longitude)

		cmd.Printf("Traffic:\n")
		cmd.Printf("  Outgoing:\t%v\n", humanize.IBytes(server.OutgoingTraffic))
		cmd.Printf("  Ingoing:\t%v\n", humanize.IBytes(server.IngoingTraffic))
		cmd.Printf("  Included:\t%v\n", humanize.IBytes(server.IncludedTraffic))

		if server.BackupWindow != "" {
			cmd.Printf("Backup Window:\t%s\n", server.BackupWindow)
		} else {
			cmd.Printf("Backup Window:\tBackups disabled\n")
		}

		if server.RescueEnabled {
			cmd.Printf("Rescue System:\tenabled\n")
		} else {
			cmd.Printf("Rescue System:\tdisabled\n")
		}

		cmd.Printf("ISO:\n")
		if server.ISO != nil {
			cmd.Printf("  ID:\t\t%d\n", server.ISO.ID)
			cmd.Printf("  Name:\t\t%s\n", server.ISO.Name)
			cmd.Printf("  Description:\t%s\n", server.ISO.Description)
			cmd.Printf("  Type:\t\t%s\n", server.ISO.Type)
		} else {
			cmd.Printf("  No ISO attached\n")
		}

		cmd.Printf("Protection:\n")
		cmd.Printf("  Delete:\t%s\n", util.YesNo(server.Protection.Delete))
		cmd.Printf("  Rebuild:\t%s\n", util.YesNo(server.Protection.Rebuild))

		cmd.Print("Labels:\n")
		if len(server.Labels) == 0 {
			cmd.Print("  No labels\n")
		} else {
			for key, value := range server.Labels {
				cmd.Printf("  %s: %s\n", key, value)
			}
		}

		cmd.Print("Placement Group:\n")
		if server.PlacementGroup != nil {
			cmd.Printf("  ID:\t\t%d\n", server.PlacementGroup.ID)
			cmd.Printf("  Name:\t\t%s\n", server.PlacementGroup.Name)
			cmd.Printf("  Type:\t\t%s\n", server.PlacementGroup.Type)
		} else {
			cmd.Print("  No Placement Group set\n")
		}

		return nil
	},
}
View Source
var DetachFromNetworkCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "detach-from-network --network <network> <server>",
			Short:                 "Detach a server from a network",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		cmd.Flags().StringP("network", "n", "", "Network (ID or name) (required)")
		_ = cmd.RegisterFlagCompletionFunc("network", cmpl.SuggestCandidatesF(client.Network().Names))
		_ = cmd.MarkFlagRequired("network")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}
		networkIDOrName, _ := cmd.Flags().GetString("network")
		network, _, err := s.Client().Network().Get(s, networkIDOrName)
		if err != nil {
			return err
		}
		if network == nil {
			return fmt.Errorf("network not found: %s", networkIDOrName)
		}

		opts := hcloud.ServerDetachFromNetworkOpts{
			Network: network,
		}
		action, _, err := s.Client().Server().DetachFromNetwork(s, server, opts)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d detached from network %d\n", server.ID, network.ID)
		return nil
	},
}
View Source
var DetachISOCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "detach-iso <server>",
			Short:                 "Detach an ISO from a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().DetachISO(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("ISO detached from server %d\n", server.ID)
		return nil
	},
}
View Source
var DisableBackupCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "disable-backup <server>",
			Short:                 "Disable backup for a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().DisableBackup(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Backup disabled for server %d\n", server.ID)
		return nil
	},
}
View Source
var DisableProtectionCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:   "disable-protection <server> <protection-level>...",
			Short: "Disable resource protection for a server",
			ValidArgsFunction: cmpl.SuggestArgs(
				cmpl.SuggestCandidatesF(client.Server().Names),
				cmpl.SuggestCandidates("delete", "rebuild"),
			),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		opts, err := getChangeProtectionOpts(false, args[1:])
		if err != nil {
			return err
		}

		return changeProtection(s, cmd, server, false, opts)
	},
}
View Source
var DisableRescueCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "disable-rescue <server>",
			Short:                 "Disable rescue for a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().DisableRescue(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Rescue disabled for server %d\n", server.ID)
		return nil
	},
}
View Source
var EnableBackupCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "enable-backup <server>",
			Short:                 "Enable backup for a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		cmd.Flags().String(
			"window", "",
			"(deprecated) The time window for the daily backup to run. All times are in UTC. 22-02 means that the backup will be started between 10 PM and 2 AM.")
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		window, _ := cmd.Flags().GetString("window")
		if window != "" {
			cmd.Print("[WARN] The ability to specify a backup window when enabling backups has been removed. Ignoring flag.\n")
		}

		action, _, err := s.Client().Server().EnableBackup(s, server, "")
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Backup enabled for server %d\n", server.ID)
		return nil
	},
}
View Source
var EnableProtectionCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:   "enable-protection <server> <protection-level>...",
			Short: "Enable resource protection for a server",
			ValidArgsFunction: cmpl.SuggestArgs(
				cmpl.SuggestCandidatesF(client.Server().Names),
				cmpl.SuggestCandidates("delete", "rebuild"),
			),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		opts, err := getChangeProtectionOpts(true, args[1:])
		if err != nil {
			return err
		}

		return changeProtection(s, cmd, server, true, opts)
	},
}
View Source
var EnableRescueCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "enable-rescue [options] <server>",
			Short:                 "Enable rescue for a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		cmd.Flags().String("type", "linux64", "Rescue type")
		_ = cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates("linux64"))

		cmd.Flags().StringSlice("ssh-key", nil, "ID or name of SSH key to inject (can be specified multiple times)")
		_ = cmd.RegisterFlagCompletionFunc("ssh-key", cmpl.SuggestCandidatesF(client.SSHKey().Names))
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		var (
			opts hcloud.ServerEnableRescueOpts
		)
		rescueType, _ := cmd.Flags().GetString("type")

		opts.Type = hcloud.ServerRescueType(rescueType)
		switch opts.Type {
		case hcloud.ServerRescueTypeLinux64:
			break
		case hcloud.ServerRescueTypeLinux32:
			return fmt.Errorf("rescue type not supported anymore: %s", opts.Type)
		default:
			return fmt.Errorf("invalid rescue type: %s", opts.Type)
		}

		sshKeys, _ := cmd.Flags().GetStringSlice("ssh-key")
		for _, sshKeyIDOrName := range sshKeys {
			sshKey, _, err := s.Client().SSHKey().Get(s, sshKeyIDOrName)
			if err != nil {
				return err
			}
			if sshKey == nil {
				return fmt.Errorf("SSH key not found: %s", sshKeyIDOrName)
			}
			opts.SSHKeys = append(opts.SSHKeys, sshKey)
		}

		result, _, err := s.Client().Server().EnableRescue(s, server, opts)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, result.Action); err != nil {
			return err
		}

		cmd.Printf("Rescue enabled for server %d with root password: %s\n", server.ID, result.RootPassword)
		return nil
	},
}
View Source
var IPCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "ip [--ipv6] <server>",
			Short:                 "Print a server's IP address",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		cmd.Flags().BoolP("ipv6", "6", false, "Print the first address of the IPv6 public server network")
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		ipv6, _ := cmd.Flags().GetBool("ipv6")
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}
		if ipv6 {
			if server.PublicNet.IPv6.IsUnspecified() {
				return fmt.Errorf("server %s has no primary IPv6", idOrName)
			}
			cmd.Println(server.PublicNet.IPv6.IP.String() + "1")
		} else {
			if server.PublicNet.IPv4.IsUnspecified() {
				return fmt.Errorf("server %s has no primary IPv4", idOrName)
			}
			cmd.Println(server.PublicNet.IPv4.IP.String())
		}
		return nil
	},
}
View Source
var LabelCmds = base.LabelCmds{
	ResourceNameSingular:   "server",
	ShortDescriptionAdd:    "Add a label to a server",
	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) {
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return nil, 0, err
		}
		if server == nil {
			return nil, 0, fmt.Errorf("server not found: %s", idOrName)
		}
		return server.Labels, server.ID, nil
	},
	SetLabels: func(s state.State, id int64, labels map[string]string) error {
		opts := hcloud.ServerUpdateOpts{
			Labels: labels,
		}
		_, _, err := s.Client().Server().Update(s, &hcloud.Server{ID: id}, opts)
		return err
	},
}
View Source
var ListCmd = base.ListCmd{
	ResourceNamePlural: "Servers",
	JSONKeyGetByName:   "servers",
	DefaultColumns:     []string{"id", "name", "status", "ipv4", "ipv6", "private_net", "datacenter", "age"},
	SortOption:         config.OptionSortServer,

	AdditionalFlags: func(cmd *cobra.Command) {
		cmd.Flags().StringSlice("status", nil, "Only servers with one of these statuses are displayed")
		_ = cmd.RegisterFlagCompletionFunc("status", cmpl.SuggestCandidates(serverStatusStrings...))
	},

	Fetch: func(s state.State, flags *pflag.FlagSet, listOpts hcloud.ListOpts, sorts []string) ([]interface{}, error) {
		statuses, _ := flags.GetStringSlice("status")

		opts := hcloud.ServerListOpts{ListOpts: listOpts}
		if len(sorts) > 0 {
			opts.Sort = sorts
		}
		if len(statuses) > 0 {
			for _, status := range statuses {
				if slices.Contains(serverStatusStrings, status) {
					opts.Status = append(opts.Status, hcloud.ServerStatus(status))
				} else {
					return nil, fmt.Errorf("invalid status: %s", status)
				}
			}
		}
		servers, err := s.Client().Server().AllWithOpts(s, opts)

		var resources []interface{}
		for _, r := range servers {
			resources = append(resources, r)
		}
		return resources, err
	},

	OutputTable: func(t *output.Table, client hcapi2.Client) {
		t.
			AddAllowedFields(hcloud.Server{}).
			AddFieldFn("ipv4", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				if server.PublicNet.IPv4.IsUnspecified() {
					return "-"
				}
				return server.PublicNet.IPv4.IP.String()
			})).
			AddFieldFn("ipv6", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				if server.PublicNet.IPv6.IsUnspecified() {
					return "-"
				}
				return server.PublicNet.IPv6.Network.String()
			})).
			AddFieldFn("included_traffic", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return humanize.IBytes(server.IncludedTraffic)
			})).
			AddFieldFn("ingoing_traffic", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return humanize.IBytes(server.IngoingTraffic)
			})).
			AddFieldFn("outgoing_traffic", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return humanize.IBytes(server.OutgoingTraffic)
			})).
			AddFieldFn("datacenter", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return server.Datacenter.Name
			})).
			AddFieldFn("location", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return server.Datacenter.Location.Name
			})).
			AddFieldFn("labels", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return util.LabelsToString(server.Labels)
			})).
			AddFieldFn("type", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return server.ServerType.Name
			})).
			AddFieldFn("volumes", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				var volumes []string
				for _, volume := range server.Volumes {
					volumeID := strconv.FormatInt(volume.ID, 10)
					volumes = append(volumes, volumeID)
				}
				return strings.Join(volumes, ", ")
			})).
			AddFieldFn("private_net", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				var networks []string
				for _, network := range server.PrivateNet {
					networks = append(networks, fmt.Sprintf("%s (%s)", network.IP.String(), client.Network().Name(network.Network.ID)))
				}
				return util.NA(strings.Join(networks, ", "))
			})).
			AddFieldFn("protection", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				var protection []string
				if server.Protection.Delete {
					protection = append(protection, "delete")
				}
				if server.Protection.Rebuild {
					protection = append(protection, "rebuild")
				}
				return strings.Join(protection, ", ")
			})).
			AddFieldFn("created", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return util.Datetime(server.Created)
			})).
			AddFieldFn("age", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				return util.Age(server.Created, time.Now())
			})).
			AddFieldFn("placement_group", output.FieldFn(func(obj interface{}) string {
				server := obj.(*hcloud.Server)
				if server.PlacementGroup == nil {
					return "-"
				}
				return server.PlacementGroup.Name
			}))
	},

	Schema: func(resources []interface{}) interface{} {
		serversSchema := make([]schema.Server, 0, len(resources))
		for _, resource := range resources {
			server := resource.(*hcloud.Server)
			serversSchema = append(serversSchema, hcloud.SchemaFromServer(server))
		}
		return serversSchema
	},
}
View Source
var MetricsCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   fmt.Sprintf("metrics [options] (--type <%s>)... <server>", strings.Join(metricTypeStrings, "|")),
			Short:                 "[ALPHA] Metrics from a Server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().StringSlice("type", nil, "Types of metrics you want to show")
		_ = cmd.MarkFlagRequired("type")
		_ = cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates(metricTypeStrings...))

		cmd.Flags().String("start", "", "ISO 8601 timestamp")
		cmd.Flags().String("end", "", "ISO 8601 timestamp")

		output.AddFlag(cmd, output.OptionJSON(), output.OptionYAML())
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		outputFlags := output.FlagsForCommand(cmd)

		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		metricTypesStr, _ := cmd.Flags().GetStringSlice("type")
		var metricTypes []hcloud.ServerMetricType
		for _, t := range metricTypesStr {
			if slices.Contains(metricTypeStrings, t) {
				metricTypes = append(metricTypes, hcloud.ServerMetricType(t))
			} else {
				return fmt.Errorf("invalid metric type: %s", t)
			}
		}

		start, _ := cmd.Flags().GetString("start")
		startTime := time.Now().Add(-30 * time.Minute)
		if start != "" {
			startTime, err = time.Parse(time.RFC3339, start)
			if err != nil {
				return fmt.Errorf("start date has an invalid format. It should be ISO 8601, like: %s", time.Now().Format(time.RFC3339))
			}
		}

		end, _ := cmd.Flags().GetString("end")
		endTime := time.Now()
		if end != "" {
			endTime, err = time.Parse(time.RFC3339, end)
			if err != nil {
				return fmt.Errorf("end date has an invalid format. It should be ISO 8601, like: %s", time.Now().Format(time.RFC3339))
			}
		}

		m, resp, err := s.Client().Server().GetMetrics(s, server, hcloud.ServerGetMetricsOpts{
			Types: metricTypes,
			Start: startTime,
			End:   endTime,
		})
		if err != nil {
			return err
		}
		switch {
		case outputFlags.IsSet("json") || outputFlags.IsSet("yaml"):
			var schema map[string]interface{}
			if err := json.NewDecoder(resp.Body).Decode(&schema); err != nil {
				return err
			}
			if outputFlags.IsSet("json") {
				return util.DescribeJSON(cmd.OutOrStdout(), schema)
			}
			return util.DescribeYAML(cmd.OutOrStdout(), schema)
		default:
			var keys []string
			for k := range m.TimeSeries {
				keys = append(keys, k)
			}
			sort.Strings(keys)
			for _, k := range keys {
				if len(m.TimeSeries[k]) == 0 {
					cmd.Printf("Currently there are no metrics available. Please try it again later.")
					return nil
				}
				cmd.Printf("Server: %s \t Metric: %s \t Start: %s \t End: %s\n", server.Name, k, m.Start.String(), m.End.String())
				var data []float64
				for _, m := range m.TimeSeries[k] {
					d, _ := strconv.ParseFloat(m.Value, 64)
					data = append(data, d)
				}
				graph := asciigraph.Plot(data, asciigraph.Height(20), asciigraph.Width(100))
				cmd.Println(graph)
				cmd.Printf("\n\n")
			}
		}
		return nil
	},
}
View Source
var PoweroffCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "poweroff <server>",
			Short:                 "Poweroff a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().Poweroff(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d stopped\n", server.ID)
		return nil
	},
}
View Source
var PoweronCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "poweron <server>",
			Short:                 "Poweron a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().Poweron(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d started\n", server.ID)
		return nil
	},
}
View Source
var RebootCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "reboot <server>",
			Short:                 "Reboot a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().Reboot(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d rebooted\n", server.ID)
		return nil
	},
}
View Source
var RebuildCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "rebuild [--allow-deprecated-image] --image <image> <server>",
			Short:                 "Rebuild a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().String("image", "", "ID or name of image to rebuild from (required)")
		_ = cmd.RegisterFlagCompletionFunc("image", cmpl.SuggestCandidatesF(client.Image().Names))
		_ = cmd.MarkFlagRequired("image")
		cmd.Flags().Bool("allow-deprecated-image", false, "Enable the use of deprecated images (default: false)")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		serverIDOrName := args[0]
		server, _, err := s.Client().Server().Get(s, serverIDOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", serverIDOrName)
		}

		imageIDOrName, _ := cmd.Flags().GetString("image")

		image, _, err := s.Client().Image().GetForArchitecture(s, imageIDOrName, server.ServerType.Architecture)
		if err != nil {
			return err
		}

		if image == nil {
			return fmt.Errorf("image %s for architecture %s not found", imageIDOrName, server.ServerType.Architecture)
		}

		allowDeprecatedImage, _ := cmd.Flags().GetBool("allow-deprecated-image")
		if !image.Deprecated.IsZero() {
			if allowDeprecatedImage {
				cmd.Printf("Attention: image %s is deprecated. It will continue to be available until %s.\n", image.Name, image.Deprecated.AddDate(0, 3, 0).Format(time.DateOnly))
			} else {
				return fmt.Errorf("image %s is deprecated, please use --allow-deprecated-image to create a server with this image. It will continue to be available until %s", image.Name, image.Deprecated.AddDate(0, 3, 0).Format(time.DateOnly))
			}
		}

		opts := hcloud.ServerRebuildOpts{
			Image: image,
		}
		result, _, err := s.Client().Server().RebuildWithResult(s, server, opts)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, result.Action); err != nil {
			return err
		}

		cmd.Printf("Server %d rebuilt with image %s\n", server.ID, image.Name)

		if result.RootPassword != "" {
			cmd.Printf("Root password: %s\n", result.RootPassword)
		}

		return nil
	},
}
View Source
var RemoveFromPlacementGroupCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:               "remove-from-placement-group <server>",
			Short:             "Removes a server from a placement group",
			ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
		}

		return cmd
	},

	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().RemoveFromPlacementGroup(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d removed from placement group\n", server.ID)
		return nil
	},
}
View Source
var RequestConsoleCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "request-console [options] <server>",
			Short:                 "Request a WebSocket VNC console for a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		output.AddFlag(cmd, output.OptionJSON(), output.OptionYAML())
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		outOpts := output.FlagsForCommand(cmd)
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		result, _, err := s.Client().Server().RequestConsole(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, result.Action); err != nil {
			return err
		}

		if outOpts.IsSet("json") || outOpts.IsSet("yaml") {
			schema := struct {
				WSSURL   string `json:"wss_url"`
				Password string `json:"password"`
			}{
				WSSURL:   result.WSSURL,
				Password: result.Password,
			}

			if outOpts.IsSet("json") {
				return util.DescribeJSON(cmd.OutOrStdout(), schema)
			}
			return util.DescribeYAML(cmd.OutOrStdout(), schema)
		}

		cmd.Printf("Console for server %d:\n", server.ID)
		cmd.Printf("WebSocket URL: %s\n", result.WSSURL)
		cmd.Printf("VNC Password: %s\n", result.Password)
		return nil
	},
}
View Source
var ResetCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		return &cobra.Command{
			Use:                   "reset [options] <server>",
			Short:                 "Reset a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().Reset(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Server %d reset\n", server.ID)
		return nil
	},
}
View Source
var ResetPasswordCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "reset-password [options] <server>",
			Short:                 "Reset the root password of a server",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		output.AddFlag(cmd, output.OptionJSON(), output.OptionYAML())
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		outputFlags := output.FlagsForCommand(cmd)

		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		result, _, err := s.Client().Server().ResetPassword(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, result.Action); err != nil {
			return err
		}

		if outputFlags.IsSet("json") || outputFlags.IsSet("yaml") {
			schema := make(map[string]interface{})
			schema["root_password"] = result.RootPassword
			if outputFlags.IsSet("json") {
				return util.DescribeJSON(cmd.OutOrStdout(), schema)
			}
			return util.DescribeYAML(cmd.OutOrStdout(), schema)
		}

		cmd.Printf("Password of server %d reset to: %s\n", server.ID, result.RootPassword)
		return nil
	},
}
View Source
var SSHCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "ssh [options] <server> [--] [ssh options] [command [argument...]]",
			Short:                 "Spawn an SSH connection for the server",
			Args:                  util.ValidateLenient,
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}
		cmd.Flags().SetInterspersed(false)
		cmd.Flags().Bool("ipv6", false, "Establish SSH connection to IPv6 address")
		cmd.Flags().StringP("user", "u", "root", "Username for SSH connection")
		cmd.Flags().IntP("port", "p", 22, "Port for SSH connection")
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		useIPv6, _ := cmd.Flags().GetBool("ipv6")
		user, _ := cmd.Flags().GetString("user")
		port, _ := cmd.Flags().GetInt("port")

		ipAddress := server.PublicNet.IPv4.IP
		if server.PublicNet.IPv4.IsUnspecified() || useIPv6 {
			if server.PublicNet.IPv6.IsUnspecified() {
				return fmt.Errorf("server %s does not have a assigned primary ipv4 or ipv6", idOrName)
			}
			ipAddress = server.PublicNet.IPv6.Network.IP

			ipAddress[15]++
		}

		sshArgs := []string{"-l", user, "-p", strconv.Itoa(port), ipAddress.String()}
		sshCommand := exec.Command(SSHPath, append(sshArgs, args[1:]...)...)
		sshCommand.Stdin = os.Stdin
		sshCommand.Stdout = os.Stdout
		sshCommand.Stderr = os.Stderr

		if err := sshCommand.Run(); err != nil {
			var exitError *exec.ExitError
			if errors.As(err, &exitError) {
				waitStatus := exitError.Sys().(syscall.WaitStatus)
				os.Exit(waitStatus.ExitStatus())
			}
			return err
		}

		return nil
	},
}
View Source
var SSHPath = "ssh"
View Source
var SetRDNSCmd = base.SetRdnsCmd{
	ResourceNameSingular: "Server",
	ShortDescription:     "Change reverse DNS of a Server",
	NameSuggestions:      func(c hcapi2.Client) func() []string { return c.Server().Names },
	Fetch: func(s state.State, _ *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
		return s.Client().Server().Get(s, idOrName)
	},
	GetDefaultIP: func(resource interface{}) net.IP {
		server := resource.(*hcloud.Server)
		return server.PublicNet.IPv4.IP
	},
}
View Source
var ShutdownCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {

		const description = "Shuts down a Server gracefully by sending an ACPI shutdown request. " +
			"The Server operating system must support ACPI and react to the request, " +
			"otherwise the Server will not shut down. Use the --wait flag to wait for the " +
			"server to shut down before returning."

		cmd := &cobra.Command{
			Use:                   "shutdown [options] <server>",
			Short:                 "Shutdown a server",
			Long:                  description,
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.Server().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().Bool("wait", false, "Wait for the server to shut down before exiting")
		cmd.Flags().Duration("wait-timeout", 30*time.Second, "Timeout for waiting for off state after shutdown")

		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {

		wait, _ := cmd.Flags().GetBool("wait")
		timeout, _ := cmd.Flags().GetDuration("wait-timeout")

		idOrName := args[0]
		server, _, err := s.Client().Server().Get(s, idOrName)
		if err != nil {
			return err
		}
		if server == nil {
			return fmt.Errorf("server not found: %s", idOrName)
		}

		action, _, err := s.Client().Server().Shutdown(s, server)
		if err != nil {
			return err
		}

		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}

		cmd.Printf("Sent shutdown signal to server %d\n", server.ID)

		if wait {
			start := time.Now()

			interval, _ := cmd.Flags().GetDuration("poll-interval")
			if interval < time.Second {
				interval = time.Second
			}

			ticker := time.NewTicker(interval)
			defer ticker.Stop()

			progress := ui.NewProgress(
				os.Stderr,
				ui.FakeActionMessage("Waiting for server to shut down"),
				ui.ActionResourcesMessage(&hcloud.ActionResource{ID: server.ID, Type: hcloud.ActionResourceTypeServer}),
			)
			progress.Start()

			for server.Status != hcloud.ServerStatusOff {
				if now := <-ticker.C; now.Sub(start) >= timeout {
					progress.SetError()
					return errors.New("failed to shut down server")
				}

				server, _, err = s.Client().Server().GetByID(s, server.ID)
				if err != nil {
					progress.SetError()
					return err
				}
			}

			progress.SetSuccess()

			cmd.Printf("Server %d shut down\n", server.ID)
		}

		return nil
	},
}
View Source
var UpdateCmd = base.UpdateCmd{
	ResourceNameSingular: "Server",
	ShortDescription:     "Update a Server",
	NameSuggestions:      func(c hcapi2.Client) func() []string { return c.Server().Names },
	Fetch: func(s state.State, _ *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
		return s.Client().Server().Get(s, idOrName)
	},
	DefineFlags: func(cmd *cobra.Command) {
		cmd.Flags().String("name", "", "Server name")
	},
	Update: func(s state.State, _ *cobra.Command, resource interface{}, flags map[string]pflag.Value) error {
		floatingIP := resource.(*hcloud.Server)
		updOpts := hcloud.ServerUpdateOpts{
			Name: flags["name"].String(),
		}
		_, _, err := s.Client().Server().Update(s, floatingIP, updOpts)
		if err != nil {
			return err
		}
		return nil
	},
}

Functions

func NewCommand

func NewCommand(s state.State) *cobra.Command

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL