instances.go raw

   1  package linodego
   2  
   3  import (
   4  	"context"
   5  	"encoding/json"
   6  	"fmt"
   7  	"net"
   8  	"time"
   9  
  10  	"github.com/linode/linodego/internal/parseabletime"
  11  )
  12  
  13  type InterfaceGeneration string
  14  
  15  const (
  16  	GenerationLegacyConfig InterfaceGeneration = "legacy_config"
  17  	GenerationLinode       InterfaceGeneration = "linode"
  18  )
  19  
  20  /*
  21   * https://techdocs.akamai.com/linode-api/reference/post-linode-instance
  22   */
  23  
  24  // InstanceStatus constants start with Instance and include Linode API Instance Status values
  25  type InstanceStatus string
  26  
  27  // InstanceStatus constants reflect the current status of an Instance
  28  const (
  29  	InstanceBooting      InstanceStatus = "booting"
  30  	InstanceRunning      InstanceStatus = "running"
  31  	InstanceOffline      InstanceStatus = "offline"
  32  	InstanceShuttingDown InstanceStatus = "shutting_down"
  33  	InstanceRebooting    InstanceStatus = "rebooting"
  34  	InstanceProvisioning InstanceStatus = "provisioning"
  35  	InstanceDeleting     InstanceStatus = "deleting"
  36  	InstanceMigrating    InstanceStatus = "migrating"
  37  	InstanceRebuilding   InstanceStatus = "rebuilding"
  38  	InstanceCloning      InstanceStatus = "cloning"
  39  	InstanceRestoring    InstanceStatus = "restoring"
  40  	InstanceResizing     InstanceStatus = "resizing"
  41  )
  42  
  43  type InstanceMigrationType string
  44  
  45  const (
  46  	WarmMigration InstanceMigrationType = "warm"
  47  	ColdMigration InstanceMigrationType = "cold"
  48  )
  49  
  50  // Instance represents a linode object
  51  type Instance struct {
  52  	ID              int                     `json:"id"`
  53  	Created         *time.Time              `json:"-"`
  54  	Updated         *time.Time              `json:"-"`
  55  	Region          string                  `json:"region"`
  56  	Alerts          *InstanceAlert          `json:"alerts"`
  57  	Backups         *InstanceBackup         `json:"backups"`
  58  	Image           string                  `json:"image"`
  59  	Group           string                  `json:"group"`
  60  	IPv4            []*net.IP               `json:"ipv4"`
  61  	IPv6            string                  `json:"ipv6"`
  62  	Label           string                  `json:"label"`
  63  	Type            string                  `json:"type"`
  64  	Status          InstanceStatus          `json:"status"`
  65  	HasUserData     bool                    `json:"has_user_data"`
  66  	Hypervisor      string                  `json:"hypervisor"`
  67  	HostUUID        string                  `json:"host_uuid"`
  68  	Specs           *InstanceSpec           `json:"specs"`
  69  	WatchdogEnabled bool                    `json:"watchdog_enabled"`
  70  	Tags            []string                `json:"tags"`
  71  	PlacementGroup  *InstancePlacementGroup `json:"placement_group"`
  72  
  73  	// NOTE: Disk encryption may not currently be available to all users.
  74  	DiskEncryption InstanceDiskEncryption `json:"disk_encryption"`
  75  
  76  	LKEClusterID int      `json:"lke_cluster_id"`
  77  	Capabilities []string `json:"capabilities"`
  78  
  79  	// Note: Linode interfaces may not currently be available to all users.
  80  	InterfaceGeneration InterfaceGeneration `json:"interface_generation"`
  81  
  82  	// NOTE: MaintenancePolicy can only be used with v4beta.
  83  	MaintenancePolicy string `json:"maintenance_policy"`
  84  
  85  	// NOTE: Locks can only be used with v4beta.
  86  	Locks []LockType `json:"locks"`
  87  }
  88  
  89  // InstanceSpec represents a linode spec
  90  type InstanceSpec struct {
  91  	Disk               int `json:"disk"`
  92  	Memory             int `json:"memory"`
  93  	VCPUs              int `json:"vcpus"`
  94  	Transfer           int `json:"transfer"`
  95  	GPUs               int `json:"gpus"`
  96  	AcceleratedDevices int `json:"accelerated_devices"`
  97  }
  98  
  99  // InstanceAlert represents a metric alert
 100  type InstanceAlert struct {
 101  	CPU           int `json:"cpu"`
 102  	IO            int `json:"io"`
 103  	NetworkIn     int `json:"network_in"`
 104  	NetworkOut    int `json:"network_out"`
 105  	TransferQuota int `json:"transfer_quota"`
 106  }
 107  
 108  // InstanceBackup represents backup settings for an instance
 109  type InstanceBackup struct {
 110  	Available      bool       `json:"available,omitempty"` // read-only
 111  	Enabled        bool       `json:"enabled,omitempty"`   // read-only
 112  	LastSuccessful *time.Time `json:"-"`                   // read-only
 113  	Schedule       struct {
 114  		Day    string `json:"day,omitempty"`
 115  		Window string `json:"window,omitempty"`
 116  	} `json:"schedule"`
 117  }
 118  
 119  type InstanceDiskEncryption string
 120  
 121  const (
 122  	InstanceDiskEncryptionEnabled  InstanceDiskEncryption = "enabled"
 123  	InstanceDiskEncryptionDisabled InstanceDiskEncryption = "disabled"
 124  )
 125  
 126  // InstanceTransfer pool stats for a Linode Instance during the current billing month
 127  type InstanceTransfer struct {
 128  	// Bytes of transfer this instance has consumed
 129  	Used int `json:"used"`
 130  
 131  	// GB of billable transfer this instance has consumed
 132  	Billable int `json:"billable"`
 133  
 134  	// GB of transfer this instance adds to the Transfer pool
 135  	Quota int `json:"quota"`
 136  }
 137  
 138  // Deprecated: use MonthlyInstanceTransferStatsV2 for new implementations
 139  //
 140  // MonthlyInstanceTransferStats pool stats for a Linode Instance network transfer statistics for a specific month
 141  type MonthlyInstanceTransferStats struct {
 142  	// The amount of inbound public network traffic received by this Linode, in bytes, for a specific year/month.
 143  	BytesIn int `json:"bytes_in"`
 144  
 145  	// The amount of outbound public network traffic sent by this Linode, in bytes, for a specific year/month.
 146  	BytesOut int `json:"bytes_out"`
 147  
 148  	// The total amount of public network traffic sent and received by this Linode, in bytes, for a specific year/month.
 149  	BytesTotal int `json:"bytes_total"`
 150  }
 151  
 152  // MonthlyInstanceTransferStatsV2 pool stats for a Linode Instance network transfer statistics for a specific month
 153  type MonthlyInstanceTransferStatsV2 struct {
 154  	// The amount of inbound public network traffic received by this Linode, in bytes, for a specific year/month.
 155  	BytesIn uint64 `json:"bytes_in"`
 156  
 157  	// The amount of outbound public network traffic sent by this Linode, in bytes, for a specific year/month.
 158  	BytesOut uint64 `json:"bytes_out"`
 159  
 160  	// The total amount of public network traffic sent and received by this Linode, in bytes, for a specific year/month.
 161  	BytesTotal uint64 `json:"bytes_total"`
 162  }
 163  
 164  // InstancePlacementGroup represents information about the placement group
 165  // this Linode is a part of.
 166  type InstancePlacementGroup struct {
 167  	ID                   int                  `json:"id"`
 168  	Label                string               `json:"label"`
 169  	PlacementGroupType   PlacementGroupType   `json:"placement_group_type"`
 170  	PlacementGroupPolicy PlacementGroupPolicy `json:"placement_group_policy"`
 171  	MigratingTo          *int                 `json:"migrating_to"` // read-only
 172  }
 173  
 174  // InstanceMetadataOptions specifies various Instance creation fields
 175  // that relate to the Linode Metadata service.
 176  type InstanceMetadataOptions struct {
 177  	// UserData expects a Base64-encoded string
 178  	UserData string `json:"user_data,omitempty"`
 179  }
 180  
 181  // InstancePasswordResetOptions specifies the new password for the Linode
 182  type InstancePasswordResetOptions struct {
 183  	RootPass string `json:"root_pass"`
 184  }
 185  
 186  // InstanceCreateOptions require only Region and Type
 187  type InstanceCreateOptions struct {
 188  	Region              string                   `json:"region"`
 189  	Type                string                   `json:"type"`
 190  	Label               string                   `json:"label,omitempty"`
 191  	RootPass            string                   `json:"root_pass,omitempty"`
 192  	AuthorizedKeys      []string                 `json:"authorized_keys,omitempty"`
 193  	AuthorizedUsers     []string                 `json:"authorized_users,omitempty"`
 194  	StackScriptID       int                      `json:"stackscript_id,omitempty"`
 195  	StackScriptData     map[string]string        `json:"stackscript_data,omitempty"`
 196  	BackupID            int                      `json:"backup_id,omitempty"`
 197  	Image               string                   `json:"image,omitempty"`
 198  	BackupsEnabled      bool                     `json:"backups_enabled,omitempty"`
 199  	PrivateIP           bool                     `json:"private_ip,omitempty"`
 200  	NetworkHelper       *bool                    `json:"network_helper,omitempty"`
 201  	Tags                []string                 `json:"tags,omitempty"`
 202  	Metadata            *InstanceMetadataOptions `json:"metadata,omitempty"`
 203  	FirewallID          int                      `json:"firewall_id,omitempty"`
 204  	InterfaceGeneration InterfaceGeneration      `json:"interface_generation,omitempty"`
 205  
 206  	// Linode Interfaces to create the new instance with.
 207  	// Conflicts with Interfaces.
 208  	// NOTE: Linode Interfaces may not currently be available to all users.
 209  	LinodeInterfaces []LinodeInterfaceCreateOptions `json:"-"`
 210  
 211  	// Legacy (config) Interfaces to create the new instance with.
 212  	// Conflicts with LinodeInterfaces.
 213  	Interfaces []InstanceConfigInterfaceCreateOptions `json:"-"`
 214  
 215  	// NOTE: Disk encryption may not currently be available to all users.
 216  	DiskEncryption InstanceDiskEncryption `json:"disk_encryption,omitempty"`
 217  
 218  	PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"`
 219  
 220  	// Creation fields that need to be set explicitly false, "", or 0 use pointers
 221  	SwapSize *int  `json:"swap_size,omitempty"`
 222  	Booted   *bool `json:"booted,omitempty"`
 223  
 224  	// Deprecated: group is a deprecated property denoting a group label for the Linode.
 225  	Group string `json:"group,omitempty"`
 226  
 227  	IPv4 []string `json:"ipv4,omitempty"`
 228  
 229  	// NOTE: MaintenancePolicy can only be used with v4beta.
 230  	MaintenancePolicy *string `json:"maintenance_policy,omitempty"`
 231  }
 232  
 233  // InstanceCreatePlacementGroupOptions represents the placement group
 234  // to create this Linode under.
 235  type InstanceCreatePlacementGroupOptions struct {
 236  	ID            int   `json:"id"`
 237  	CompliantOnly *bool `json:"compliant_only,omitempty"`
 238  }
 239  
 240  // InstanceUpdateOptions is an options struct used when Updating an Instance
 241  type InstanceUpdateOptions struct {
 242  	Label           string          `json:"label,omitempty"`
 243  	Backups         *InstanceBackup `json:"backups,omitempty"`
 244  	Alerts          *InstanceAlert  `json:"alerts,omitempty"`
 245  	WatchdogEnabled *bool           `json:"watchdog_enabled,omitempty"`
 246  	Tags            *[]string       `json:"tags,omitempty"`
 247  
 248  	// Deprecated: group is a deprecated property denoting a group label for the Linode.
 249  	Group *string `json:"group,omitempty"`
 250  
 251  	// NOTE: MaintenancePolicy can only be used with v4beta.
 252  	MaintenancePolicy *string `json:"maintenance_policy,omitempty"`
 253  }
 254  
 255  // MarshalJSON contains logic necessary to populate the `interfaces` field of
 256  // InstanceCreateOptions depending on whether Interfaces or LinodeInterfaces
 257  // is specified.
 258  func (i InstanceCreateOptions) MarshalJSON() ([]byte, error) {
 259  	type Mask InstanceCreateOptions
 260  
 261  	resultData := struct {
 262  		*Mask
 263  
 264  		Interfaces any `json:"interfaces,omitempty"`
 265  	}{
 266  		Mask:       (*Mask)(&i),
 267  		Interfaces: nil,
 268  	}
 269  
 270  	if i.Interfaces != nil && i.LinodeInterfaces != nil {
 271  		return nil, fmt.Errorf("fields Interfaces and LinodeInterfaces cannot be specified together")
 272  	}
 273  
 274  	if i.Interfaces != nil {
 275  		resultData.Interfaces = i.Interfaces
 276  	}
 277  
 278  	if i.LinodeInterfaces != nil {
 279  		resultData.Interfaces = i.LinodeInterfaces
 280  	}
 281  
 282  	return json.Marshal(resultData)
 283  }
 284  
 285  // UnmarshalJSON contains logic necessary to populate the Interfaces field
 286  // depending on the value of interface_generation.
 287  func (i *InstanceCreateOptions) UnmarshalJSON(b []byte) error {
 288  	type Mask InstanceCreateOptions
 289  
 290  	p := struct {
 291  		*Mask
 292  
 293  		GenericInterfaces any `json:"interfaces,omitempty"`
 294  	}{
 295  		Mask: (*Mask)(i),
 296  	}
 297  
 298  	if err := json.Unmarshal(b, &p); err != nil {
 299  		return err
 300  	}
 301  
 302  	if p.GenericInterfaces == nil {
 303  		// No interfaces were given - nothing to do here.
 304  		return nil
 305  	}
 306  
 307  	if i.InterfaceGeneration == GenerationLinode {
 308  		data := struct {
 309  			Interfaces []LinodeInterfaceCreateOptions `json:"interfaces"`
 310  		}{}
 311  
 312  		err := json.Unmarshal(b, &data)
 313  		i.LinodeInterfaces = data.Interfaces
 314  
 315  		return err
 316  	}
 317  
 318  	if i.InterfaceGeneration == GenerationLegacyConfig {
 319  		data := struct {
 320  			Interfaces []InstanceConfigInterfaceCreateOptions `json:"interfaces"`
 321  		}{}
 322  
 323  		err := json.Unmarshal(b, &data)
 324  		i.Interfaces = data.Interfaces
 325  
 326  		return err
 327  	}
 328  
 329  	return fmt.Errorf("cannot unmarshal interfaces without valid value for interface_generation")
 330  }
 331  
 332  // UnmarshalJSON implements the json.Unmarshaler interface
 333  func (i *Instance) UnmarshalJSON(b []byte) error {
 334  	type Mask Instance
 335  
 336  	p := struct {
 337  		*Mask
 338  
 339  		Created *parseabletime.ParseableTime `json:"created"`
 340  		Updated *parseabletime.ParseableTime `json:"updated"`
 341  	}{
 342  		Mask: (*Mask)(i),
 343  	}
 344  
 345  	if err := json.Unmarshal(b, &p); err != nil {
 346  		return err
 347  	}
 348  
 349  	i.Created = (*time.Time)(p.Created)
 350  	i.Updated = (*time.Time)(p.Updated)
 351  
 352  	return nil
 353  }
 354  
 355  // UnmarshalJSON implements the json.Unmarshaler interface
 356  func (backup *InstanceBackup) UnmarshalJSON(b []byte) error {
 357  	type Mask InstanceBackup
 358  
 359  	p := struct {
 360  		*Mask
 361  
 362  		LastSuccessful *parseabletime.ParseableTime `json:"last_successful"`
 363  	}{
 364  		Mask: (*Mask)(backup),
 365  	}
 366  
 367  	if err := json.Unmarshal(b, &p); err != nil {
 368  		return err
 369  	}
 370  
 371  	backup.LastSuccessful = (*time.Time)(p.LastSuccessful)
 372  
 373  	return nil
 374  }
 375  
 376  // GetUpdateOptions converts an Instance to InstanceUpdateOptions for use in UpdateInstance
 377  func (i *Instance) GetUpdateOptions() InstanceUpdateOptions {
 378  	return InstanceUpdateOptions{
 379  		Label:             i.Label,
 380  		Group:             &i.Group,
 381  		Backups:           i.Backups,
 382  		Alerts:            i.Alerts,
 383  		WatchdogEnabled:   &i.WatchdogEnabled,
 384  		Tags:              &i.Tags,
 385  		MaintenancePolicy: &i.MaintenancePolicy,
 386  	}
 387  }
 388  
 389  // InstanceCloneOptions is an options struct sent when Cloning an Instance
 390  type InstanceCloneOptions struct {
 391  	Region string `json:"region,omitempty"`
 392  	Type   string `json:"type,omitempty"`
 393  
 394  	// LinodeID is an optional existing instance to use as the target of the clone
 395  	LinodeID       int                                  `json:"linode_id,omitempty"`
 396  	Label          string                               `json:"label,omitempty"`
 397  	BackupsEnabled bool                                 `json:"backups_enabled"`
 398  	Disks          []int                                `json:"disks,omitempty"`
 399  	Configs        []int                                `json:"configs,omitempty"`
 400  	PrivateIP      bool                                 `json:"private_ip,omitempty"`
 401  	Metadata       *InstanceMetadataOptions             `json:"metadata,omitempty"`
 402  	PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"`
 403  
 404  	// Deprecated: group is a deprecated property denoting a group label for the Linode.
 405  	Group string `json:"group,omitempty"`
 406  }
 407  
 408  // InstanceResizeOptions is an options struct used when resizing an instance
 409  type InstanceResizeOptions struct {
 410  	Type          string                `json:"type"`
 411  	MigrationType InstanceMigrationType `json:"migration_type,omitempty"`
 412  
 413  	// When enabled, an instance resize will also resize a data disk if the instance has no more than one data disk and one swap disk
 414  	AllowAutoDiskResize *bool `json:"allow_auto_disk_resize,omitempty"`
 415  }
 416  
 417  // InstanceMigrateOptions is an options struct used when migrating an instance
 418  type InstanceMigrateOptions struct {
 419  	Type    InstanceMigrationType `json:"type,omitempty"`
 420  	Region  string                `json:"region,omitempty"`
 421  	Upgrade *bool                 `json:"upgrade,omitempty"`
 422  
 423  	PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"`
 424  }
 425  
 426  // ListInstances lists linode instances
 427  func (c *Client) ListInstances(ctx context.Context, opts *ListOptions) ([]Instance, error) {
 428  	return getPaginatedResults[Instance](ctx, c, "linode/instances", opts)
 429  }
 430  
 431  // GetInstance gets the instance with the provided ID
 432  func (c *Client) GetInstance(ctx context.Context, linodeID int) (*Instance, error) {
 433  	e := formatAPIPath("linode/instances/%d", linodeID)
 434  	return doGETRequest[Instance](ctx, c, e)
 435  }
 436  
 437  // GetInstanceTransfer gets the instance's network transfer pool statistics for the current month.
 438  func (c *Client) GetInstanceTransfer(ctx context.Context, linodeID int) (*InstanceTransfer, error) {
 439  	e := formatAPIPath("linode/instances/%d/transfer", linodeID)
 440  	return doGETRequest[InstanceTransfer](ctx, c, e)
 441  }
 442  
 443  // GetInstanceTransferMonthly gets the instance's network transfer pool statistics for a specific month.
 444  func (c *Client) GetInstanceTransferMonthly(ctx context.Context, linodeID, year, month int) (*MonthlyInstanceTransferStats, error) {
 445  	e := formatAPIPath("linode/instances/%d/transfer/%d/%d", linodeID, year, month)
 446  	return doGETRequest[MonthlyInstanceTransferStats](ctx, c, e)
 447  }
 448  
 449  // GetInstanceTransferMonthlyV2 gets the instance's network transfer pool statistics for a specific month.
 450  func (c *Client) GetInstanceTransferMonthlyV2(ctx context.Context, linodeID, year, month int) (*MonthlyInstanceTransferStatsV2, error) {
 451  	e := formatAPIPath("linode/instances/%d/transfer/%d/%d", linodeID, year, month)
 452  	return doGETRequest[MonthlyInstanceTransferStatsV2](ctx, c, e)
 453  }
 454  
 455  // CreateInstance creates a Linode instance
 456  func (c *Client) CreateInstance(ctx context.Context, opts InstanceCreateOptions) (*Instance, error) {
 457  	return doPOSTRequest[Instance](ctx, c, "linode/instances", opts)
 458  }
 459  
 460  // UpdateInstance updates a Linode instance
 461  func (c *Client) UpdateInstance(ctx context.Context, linodeID int, opts InstanceUpdateOptions) (*Instance, error) {
 462  	e := formatAPIPath("linode/instances/%d", linodeID)
 463  	return doPUTRequest[Instance](ctx, c, e, opts)
 464  }
 465  
 466  // RenameInstance renames an Instance
 467  func (c *Client) RenameInstance(ctx context.Context, linodeID int, label string) (*Instance, error) {
 468  	return c.UpdateInstance(ctx, linodeID, InstanceUpdateOptions{Label: label})
 469  }
 470  
 471  // DeleteInstance deletes a Linode instance
 472  func (c *Client) DeleteInstance(ctx context.Context, linodeID int) error {
 473  	e := formatAPIPath("linode/instances/%d", linodeID)
 474  	return doDELETERequest(ctx, c, e)
 475  }
 476  
 477  // BootInstance will boot a Linode instance
 478  // A configID of 0 will cause Linode to choose the last/best config
 479  func (c *Client) BootInstance(ctx context.Context, linodeID int, configID int) error {
 480  	opts := make(map[string]int)
 481  
 482  	if configID != 0 {
 483  		opts = map[string]int{"config_id": configID}
 484  	}
 485  
 486  	e := formatAPIPath("linode/instances/%d/boot", linodeID)
 487  
 488  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 489  }
 490  
 491  // CloneInstance clone an existing Instances Disks and Configuration profiles to another Linode Instance
 492  func (c *Client) CloneInstance(ctx context.Context, linodeID int, opts InstanceCloneOptions) (*Instance, error) {
 493  	e := formatAPIPath("linode/instances/%d/clone", linodeID)
 494  	return doPOSTRequest[Instance](ctx, c, e, opts)
 495  }
 496  
 497  // ResetInstancePassword resets a Linode instance's root password
 498  func (c *Client) ResetInstancePassword(ctx context.Context, linodeID int, opts InstancePasswordResetOptions) error {
 499  	e := formatAPIPath("linode/instances/%d/password", linodeID)
 500  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 501  }
 502  
 503  // RebootInstance reboots a Linode instance
 504  // A configID of 0 will cause Linode to choose the last/best config
 505  func (c *Client) RebootInstance(ctx context.Context, linodeID int, configID int) error {
 506  	opts := make(map[string]int)
 507  
 508  	if configID != 0 {
 509  		opts = map[string]int{"config_id": configID}
 510  	}
 511  
 512  	e := formatAPIPath("linode/instances/%d/reboot", linodeID)
 513  
 514  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 515  }
 516  
 517  // InstanceRebuildOptions is a struct representing the options to send to the rebuild linode endpoint
 518  type InstanceRebuildOptions struct {
 519  	Image           string                   `json:"image,omitempty"`
 520  	RootPass        string                   `json:"root_pass,omitempty"`
 521  	AuthorizedKeys  []string                 `json:"authorized_keys,omitempty"`
 522  	AuthorizedUsers []string                 `json:"authorized_users,omitempty"`
 523  	StackScriptID   int                      `json:"stackscript_id,omitempty"`
 524  	StackScriptData map[string]string        `json:"stackscript_data,omitempty"`
 525  	Booted          *bool                    `json:"booted,omitempty"`
 526  	Metadata        *InstanceMetadataOptions `json:"metadata,omitempty"`
 527  	Type            string                   `json:"type,omitempty"`
 528  
 529  	// NOTE: Disk encryption may not currently be available to all users.
 530  	DiskEncryption InstanceDiskEncryption `json:"disk_encryption,omitempty"`
 531  }
 532  
 533  // RebuildInstance Deletes all Disks and Configs on this Linode,
 534  // then deploys a new Image to this Linode with the given attributes.
 535  func (c *Client) RebuildInstance(ctx context.Context, linodeID int, opts InstanceRebuildOptions) (*Instance, error) {
 536  	e := formatAPIPath("linode/instances/%d/rebuild", linodeID)
 537  	return doPOSTRequest[Instance](ctx, c, e, opts)
 538  }
 539  
 540  // InstanceRescueOptions fields are those accepted by RescueInstance
 541  type InstanceRescueOptions struct {
 542  	Devices InstanceConfigDeviceMap `json:"devices"`
 543  }
 544  
 545  // RescueInstance reboots an instance into a safe environment for performing many system recovery and disk management tasks.
 546  // Rescue Mode is based on the Finnix recovery distribution, a self-contained and bootable Linux distribution.
 547  // You can also use Rescue Mode for tasks other than disaster recovery, such as formatting disks to use different filesystems,
 548  // copying data between disks, and downloading files from a disk via SSH and SFTP.
 549  func (c *Client) RescueInstance(ctx context.Context, linodeID int, opts InstanceRescueOptions) error {
 550  	e := formatAPIPath("linode/instances/%d/rescue", linodeID)
 551  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 552  }
 553  
 554  // ResizeInstance resizes an instance to new Linode type
 555  func (c *Client) ResizeInstance(ctx context.Context, linodeID int, opts InstanceResizeOptions) error {
 556  	e := formatAPIPath("linode/instances/%d/resize", linodeID)
 557  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 558  }
 559  
 560  // ShutdownInstance - Shutdown an instance
 561  func (c *Client) ShutdownInstance(ctx context.Context, id int) error {
 562  	return c.simpleInstanceAction(ctx, "shutdown", id)
 563  }
 564  
 565  // Deprecated: Please use UpgradeInstance instead.
 566  // MutateInstance Upgrades a Linode to its next generation.
 567  func (c *Client) MutateInstance(ctx context.Context, id int) error {
 568  	return c.simpleInstanceAction(ctx, "mutate", id)
 569  }
 570  
 571  // InstanceUpgradeOptions is a struct representing the options for upgrading a Linode
 572  type InstanceUpgradeOptions struct {
 573  	// Automatically resize disks when resizing a Linode.
 574  	// When resizing down to a smaller plan your Linode's data must fit within the smaller disk size.
 575  	AllowAutoDiskResize bool `json:"allow_auto_disk_resize"`
 576  }
 577  
 578  // UpgradeInstance upgrades a Linode to its next generation.
 579  func (c *Client) UpgradeInstance(ctx context.Context, linodeID int, opts InstanceUpgradeOptions) error {
 580  	e := formatAPIPath("linode/instances/%d/mutate", linodeID)
 581  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 582  }
 583  
 584  // MigrateInstance - Migrate an instance
 585  func (c *Client) MigrateInstance(ctx context.Context, linodeID int, opts InstanceMigrateOptions) error {
 586  	e := formatAPIPath("linode/instances/%d/migrate", linodeID)
 587  	return doPOSTRequestNoResponseBody(ctx, c, e, opts)
 588  }
 589  
 590  // simpleInstanceAction is a helper for Instance actions that take no parameters
 591  // and return empty responses `{}` unless they return a standard error
 592  func (c *Client) simpleInstanceAction(ctx context.Context, action string, linodeID int) error {
 593  	e := formatAPIPath("linode/instances/%d/%s", linodeID, action)
 594  	return doPOSTRequestNoRequestResponseBody(ctx, c, e)
 595  }
 596