package govultr import ( "context" "fmt" "net/http" "github.com/google/go-querystring/query" ) const bmPath = "/v2/bare-metals" // BareMetalServerService is the interface to interact with the Bare Metal endpoints on the Vultr API // Link : https://www.vultr.com/api/#tag/baremetal type BareMetalServerService interface { Create(ctx context.Context, bmCreate *BareMetalCreate) (*BareMetalServer, error) Get(ctx context.Context, serverID string) (*BareMetalServer, error) Update(ctx context.Context, serverID string, bmReq *BareMetalUpdate) (*BareMetalServer, error) Delete(ctx context.Context, serverID string) error List(ctx context.Context, options *ListOptions) ([]BareMetalServer, *Meta, error) GetBandwidth(ctx context.Context, serverID string) (*Bandwidth, error) GetUserData(ctx context.Context, serverID string) (*UserData, error) GetVNCUrl(ctx context.Context, serverID string) (*VNCUrl, error) ListIPv4s(ctx context.Context, serverID string, options *ListOptions) ([]IPv4, *Meta, error) ListIPv6s(ctx context.Context, serverID string, options *ListOptions) ([]IPv6, *Meta, error) Halt(ctx context.Context, serverID string) error Reboot(ctx context.Context, serverID string) error Start(ctx context.Context, serverID string) error Reinstall(ctx context.Context, serverID string) (*BareMetalServer, error) MassStart(ctx context.Context, serverList []string) error MassHalt(ctx context.Context, serverList []string) error MassReboot(ctx context.Context, serverList []string) error GetUpgrades(ctx context.Context, serverID string) (*Upgrades, error) } // BareMetalServerServiceHandler handles interaction with the Bare Metal methods for the Vultr API type BareMetalServerServiceHandler struct { client *Client } // BareMetalServer represents a Bare Metal server on Vultr type BareMetalServer struct { ID string `json:"id"` Os string `json:"os"` RAM string `json:"ram"` Disk string `json:"disk"` MainIP string `json:"main_ip"` CPUCount int `json:"cpu_count"` Region string `json:"region"` DefaultPassword string `json:"default_password"` DateCreated string `json:"date_created"` Status string `json:"status"` NetmaskV4 string `json:"netmask_v4"` GatewayV4 string `json:"gateway_v4"` Plan string `json:"plan"` V6Network string `json:"v6_network"` V6MainIP string `json:"v6_main_ip"` V6NetworkSize int `json:"v6_network_size"` MacAddress int `json:"mac_address"` Label string `json:"label"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag"` OsID int `json:"os_id"` AppID int `json:"app_id"` ImageID string `json:"image_id"` Features []string `json:"features"` Tags []string `json:"tags"` } // BareMetalCreate represents the optional parameters that can be set when creating a Bare Metal server type BareMetalCreate struct { Region string `json:"region,omitempty"` Plan string `json:"plan,omitempty"` OsID int `json:"os_id,omitempty"` StartupScriptID string `json:"script_id,omitempty"` SnapshotID string `json:"snapshot_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` Label string `json:"label,omitempty"` SSHKeyIDs []string `json:"sshkey_id,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` UserData string `json:"user_data,omitempty"` ActivationEmail *bool `json:"activation_email,omitempty"` Hostname string `json:"hostname,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag string `json:"tag,omitempty"` ReservedIPv4 string `json:"reserved_ipv4,omitempty"` PersistentPxe *bool `json:"persistent_pxe,omitempty"` Tags []string `json:"tags"` } // BareMetalUpdate represents the optional parameters that can be set when updating a Bare Metal server type BareMetalUpdate struct { OsID int `json:"os_id,omitempty"` EnableIPv6 *bool `json:"enable_ipv6,omitempty"` Label string `json:"label,omitempty"` AppID int `json:"app_id,omitempty"` ImageID string `json:"image_id,omitempty"` UserData string `json:"user_data,omitempty"` // Deprecated: Tag should no longer be used. Instead, use Tags. Tag *string `json:"tag,omitempty"` Tags []string `json:"tags"` } // BareMetalServerBandwidth represents bandwidth information for a Bare Metal server type BareMetalServerBandwidth struct { IncomingBytes int `json:"incoming_bytes"` OutgoingBytes int `json:"outgoing_bytes"` } type bareMetalsBase struct { BareMetals []BareMetalServer `json:"bare_metals"` Meta *Meta `json:"meta"` } type bareMetalBase struct { BareMetal *BareMetalServer `json:"bare_metal"` } // BMBareMetalBase ... type BMBareMetalBase struct { BareMetalBandwidth map[string]BareMetalServerBandwidth `json:"bandwidth"` } type vncBase struct { VNCUrl *VNCUrl `json:"vnc"` } // VNCUrl contains the URL for a given Bare Metals VNC type VNCUrl struct { URL string `json:"url"` } // Create a new Bare Metal server. func (b *BareMetalServerServiceHandler) Create(ctx context.Context, bmCreate *BareMetalCreate) (*BareMetalServer, error) { req, err := b.client.NewRequest(ctx, http.MethodPost, bmPath, bmCreate) if err != nil { return nil, err } bm := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bm); err != nil { return nil, err } return bm.BareMetal, nil } // Get information for a Bare Metal instance. func (b *BareMetalServerServiceHandler) Get(ctx context.Context, serverID string) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // Update a Bare Metal server func (b *BareMetalServerServiceHandler) Update(ctx context.Context, serverID string, bmReq *BareMetalUpdate) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPatch, uri, bmReq) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // Delete a Bare Metal server. func (b *BareMetalServerServiceHandler) Delete(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodDelete, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // List all Bare Metal instances in your account. func (b *BareMetalServerServiceHandler) List(ctx context.Context, options *ListOptions) ([]BareMetalServer, *Meta, error) { req, err := b.client.NewRequest(ctx, http.MethodGet, bmPath, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() bms := new(bareMetalsBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, nil, err } return bms.BareMetals, bms.Meta, nil } // GetBandwidth used by a Bare Metal server. func (b *BareMetalServerServiceHandler) GetBandwidth(ctx context.Context, serverID string) (*Bandwidth, error) { uri := fmt.Sprintf("%s/%s/bandwidth", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } bms := new(Bandwidth) if err = b.client.DoWithContext(ctx, req, &bms); err != nil { return nil, err } return bms, nil } // GetUserData for a Bare Metal server. The userdata returned will be in base64 encoding. func (b *BareMetalServerServiceHandler) GetUserData(ctx context.Context, serverID string) (*UserData, error) { uri := fmt.Sprintf("%s/%s/user-data", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } userData := new(userDataBase) if err = b.client.DoWithContext(ctx, req, userData); err != nil { return nil, err } return userData.UserData, nil } // GetVNCUrl gets the vnc url for a given Bare Metal server. func (b *BareMetalServerServiceHandler) GetVNCUrl(ctx context.Context, serverID string) (*VNCUrl, error) { uri := fmt.Sprintf("%s/%s/vnc", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } vnc := new(vncBase) if err = b.client.DoWithContext(ctx, req, vnc); err != nil { return nil, err } return vnc.VNCUrl, nil } // ListIPv4s information of a Bare Metal server. // IP information is only available for Bare Metal servers in the "active" state. func (b *BareMetalServerServiceHandler) ListIPv4s(ctx context.Context, serverID string, options *ListOptions) ([]IPv4, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv4", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ipv4 := new(ipBase) if err = b.client.DoWithContext(ctx, req, ipv4); err != nil { return nil, nil, err } return ipv4.IPv4s, ipv4.Meta, nil } // ListIPv6s information of a Bare Metal server. // IP information is only available for Bare Metal servers in the "active" state. // If the Bare Metal server does not have IPv6 enabled, then an empty array is returned. func (b *BareMetalServerServiceHandler) ListIPv6s(ctx context.Context, serverID string, options *ListOptions) ([]IPv6, *Meta, error) { uri := fmt.Sprintf("%s/%s/ipv6", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, nil, err } newValues, err := query.Values(options) if err != nil { return nil, nil, err } req.URL.RawQuery = newValues.Encode() ipv6 := new(ipBase) if err = b.client.DoWithContext(ctx, req, ipv6); err != nil { return nil, nil, err } return ipv6.IPv6s, ipv6.Meta, nil } // Halt a Bare Metal server. // This is a hard power off, meaning that the power to the machine is severed. // The data on the machine will not be modified, and you will still be billed for the machine. func (b *BareMetalServerServiceHandler) Halt(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/halt", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Reboot a Bare Metal server. This is a hard reboot, which means that the server is powered off, then back on. func (b *BareMetalServerServiceHandler) Reboot(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/reboot", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Start a Bare Metal server. func (b *BareMetalServerServiceHandler) Start(ctx context.Context, serverID string) error { uri := fmt.Sprintf("%s/%s/start", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // Reinstall the operating system on a Bare Metal server. // All data will be permanently lost, but the IP address will remain the same. func (b *BareMetalServerServiceHandler) Reinstall(ctx context.Context, serverID string) (*BareMetalServer, error) { uri := fmt.Sprintf("%s/%s/reinstall", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodPost, uri, nil) if err != nil { return nil, err } bms := new(bareMetalBase) if err = b.client.DoWithContext(ctx, req, bms); err != nil { return nil, err } return bms.BareMetal, nil } // MassStart will start a list of Bare Metal servers the machine is already running, it will be restarted. func (b *BareMetalServerServiceHandler) MassStart(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/start", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // MassHalt a list of Bare Metal servers. func (b *BareMetalServerServiceHandler) MassHalt(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/halt", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // MassReboot a list of Bare Metal servers. func (b *BareMetalServerServiceHandler) MassReboot(ctx context.Context, serverList []string) error { uri := fmt.Sprintf("%s/reboot", bmPath) reqBody := RequestBody{"baremetal_ids": serverList} req, err := b.client.NewRequest(ctx, http.MethodPost, uri, reqBody) if err != nil { return err } return b.client.DoWithContext(ctx, req, nil) } // GetUpgrades that are available for a Bare Metal server. func (b *BareMetalServerServiceHandler) GetUpgrades(ctx context.Context, serverID string) (*Upgrades, error) { uri := fmt.Sprintf("%s/%s/upgrades", bmPath, serverID) req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } upgrades := new(upgradeBase) if err = b.client.DoWithContext(ctx, req, upgrades); err != nil { return nil, err } return upgrades.Upgrades, nil }