server

package
v1.43.1 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: MIT Imports: 30 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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,
		}
		for _, aliasIP := range aliasIPs {
			opts.AliasIPs = append(opts.AliasIPs, aliasIP)
		}
		action, _, err := s.Client().Server().AttachToNetwork(s, server, opts)

		if err != nil {
			return err
		}

		if err := s.ActionProgress(cmd, s, 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 {
		clear, _ := 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 clear {
			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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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, args []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.ActionProgress(cmd, s, result.Action); err != nil {
			return nil, nil, err
		}
		if err := s.WaitForActions(cmd, s, 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.ActionProgress(cmd, s, 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 {
			var networks []string
			for _, network := range server.PrivateNet {
				networks = append(networks, fmt.Sprintf("- %s (%s)", network.IP.String(), s.Client().Network().Name(network.Network.ID)))
			}
			cmd.Printf("Private Networks:\n\t%s\n", strings.Join(networks, "\n"))
		}

		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(client 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.ActionProgress(cmd, s, 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",
	ShortDescription:     "Delete a server",
	NameSuggestions:      func(c hcapi2.Client) func() []string { return c.Server().Names },
	Fetch: func(s state.State, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
		return s.Client().Server().Get(s, idOrName)
	},
	Delete: func(s state.State, cmd *cobra.Command, resource interface{}) error {
		server := resource.(*hcloud.Server)
		result, _, err := s.Client().Server().DeleteWithResult(s, server)
		if err != nil {
			return err
		}

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

		return 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, cmd *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.Printf(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: %v", 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: %v", 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: %v", 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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, err := 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"},

	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(client hcapi2.Client) *output.Table {
		return output.NewTable().
			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(schema)
			} else {
				return util.DescribeYAML(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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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(schema)
			} else {
				return util.DescribeYAML(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.ActionProgress(cmd, s, 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.ActionProgress(cmd, s, 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(schema)
			} else {
				return util.DescribeYAML(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> [command...]",
			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(s.Config().SSHPath(), append(sshArgs, args[1:]...)...)
		sshCommand.Stdin = os.Stdin
		sshCommand.Stdout = os.Stdout
		sshCommand.Stderr = os.Stderr

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

		return nil
	},
}
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, cmd *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.ActionProgress(cmd, s, action); err != nil {
			return err
		}

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

		if wait {
			start := time.Now()
			errCh := make(chan error)

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

			go func() {
				defer close(errCh)

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

				for server.Status != hcloud.ServerStatusOff {
					if now := <-ticker.C; now.Sub(start) >= timeout {
						errCh <- errors.New("failed to shut down server")
						return
					}
					server, _, err = s.Client().Server().GetByID(s, server.ID)
					if err != nil {
						errCh <- err
						return
					}
				}

				errCh <- nil
			}()

			if err := state.DisplayProgressCircle(cmd, errCh, "Waiting for server to shut down"); err != nil {
				return err
			}

			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, cmd *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, cmd *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