From d067af04c8a60aae942287fed1e91cb4401f05a4 Mon Sep 17 00:00:00 2001 From: Josh Vasilevsky Date: Wed, 27 Nov 2024 14:39:56 -0500 Subject: [PATCH] LBAAS-3552: add lb ipv6 field --- .../loadbalancer/datasource_loadbalancer.go | 4 + .../loadbalancer/resource_loadbalancer.go | 9 + go.mod | 2 +- go.sum | 2 + .../github.com/digitalocean/godo/CHANGELOG.md | 20 ++ .../github.com/digitalocean/godo/apps.gen.go | 4 +- vendor/github.com/digitalocean/godo/apps.go | 49 +++++ .../github.com/digitalocean/godo/databases.go | 40 ++-- .../digitalocean/godo/droplet_actions.go | 38 ++++ .../github.com/digitalocean/godo/droplets.go | 172 +++++++++++++++--- vendor/github.com/digitalocean/godo/godo.go | 80 ++++---- .../digitalocean/godo/load_balancers.go | 1 + .../github.com/digitalocean/godo/registry.go | 120 ++++++++++++ .../digitalocean/godo/reserved_ipv6.go | 132 ++++++++++++++ .../godo/reserved_ipv6_actions.go | 57 ++++++ .../github.com/digitalocean/godo/strings.go | 24 +++ vendor/modules.txt | 2 +- 17 files changed, 677 insertions(+), 79 deletions(-) create mode 100644 vendor/github.com/digitalocean/godo/reserved_ipv6.go create mode 100644 vendor/github.com/digitalocean/godo/reserved_ipv6_actions.go diff --git a/digitalocean/loadbalancer/datasource_loadbalancer.go b/digitalocean/loadbalancer/datasource_loadbalancer.go index 9a3fc316a..b382b5098 100644 --- a/digitalocean/loadbalancer/datasource_loadbalancer.go +++ b/digitalocean/loadbalancer/datasource_loadbalancer.go @@ -432,6 +432,10 @@ func dataSourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resou d.Set("type", foundLoadbalancer.Type) d.Set("network", foundLoadbalancer.Network) + if foundLoadbalancer.IPv6 != "" { + d.Set("ipv6", foundLoadbalancer.IPv6) + } + if err := d.Set("droplet_ids", flattenDropletIds(foundLoadbalancer.DropletIDs)); err != nil { return diag.Errorf("[DEBUG] Error setting Load Balancer droplet_ids - error: %#v", err) } diff --git a/digitalocean/loadbalancer/resource_loadbalancer.go b/digitalocean/loadbalancer/resource_loadbalancer.go index 460277a77..af3643a04 100644 --- a/digitalocean/loadbalancer/resource_loadbalancer.go +++ b/digitalocean/loadbalancer/resource_loadbalancer.go @@ -394,6 +394,11 @@ func resourceDigitalOceanLoadBalancerV0() *schema.Resource { Computed: true, }, + "ipv6": { + Type: schema.TypeString, + Computed: true, + }, + "status": { Type: schema.TypeString, Computed: true, @@ -741,6 +746,10 @@ func resourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resourc d.Set("http_idle_timeout_seconds", loadbalancer.HTTPIdleTimeoutSeconds) d.Set("project_id", loadbalancer.ProjectID) + if loadbalancer.IPv6 != "" { + d.Set("ipv6", loadbalancer.IPv6) + } + if loadbalancer.SizeUnit > 0 { d.Set("size_unit", loadbalancer.SizeUnit) } else { diff --git a/go.mod b/go.mod index a45200125..1627d3d4a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/digitalocean/terraform-provider-digitalocean require ( github.com/aws/aws-sdk-go v1.42.18 - github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887 + github.com/digitalocean/godo v1.131.0 github.com/hashicorp/awspolicyequivalence v1.5.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-uuid v1.0.3 diff --git a/go.sum b/go.sum index ce527179e..da05f45ac 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887 h1:kdXNbMfHEDbQilcqllKkNrJ85ftyJSvSDpsQvzrhHbg= github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= +github.com/digitalocean/godo v1.131.0 h1:0WHymufAV5avpodT0h5/pucUVfO4v7biquOIqhLeROY= +github.com/digitalocean/godo v1.131.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 667255b2a..3fb2a160c 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,25 @@ # Change Log +## [v1.131.0] - 2024-11-25 + +- #760 - @jvasilevsky - LBAAS: add ipv6 field to loadbalancer model +- #759 - @imaskm - Add reserved ipv6 changes as Beta +- #758 - @dvigueras - Add Rules field to create Databases with Firewall Rules +- #751 - @blesswinsamuel - APPS-9766 Add method to restart apps + + +## [v1.130.0] - 2024-11-14 + +- #755 - @vsharma6855 - Add Missing Database Configs for Postgresql and MYSQL +- #754 - @blesswinsamuel - APPS-9858 Add method to obtain websocket URL to get console access into components + +## [v1.129.0] - 2024-11-06 + +- #752 - @andrewsomething - Support maps in Stringify +- #749 - @loosla - [droplets]: add droplet backup policies +- #730 - @rak16 - DOCR-1201: Add new RegistriesService to support methods for multiple-registry open beta +- #748 - @andrewsomething - Support Droplet GPU information + ## [v1.128.0] - 2024-10-24 - #746 - @blesswinsamuel - Add archive field to AppSpec to archive/restore apps diff --git a/vendor/github.com/digitalocean/godo/apps.gen.go b/vendor/github.com/digitalocean/godo/apps.gen.go index 0232ae943..6fc029cb9 100644 --- a/vendor/github.com/digitalocean/godo/apps.gen.go +++ b/vendor/github.com/digitalocean/godo/apps.gen.go @@ -466,8 +466,7 @@ type AppLogDestinationSpecPapertrail struct { type AppMaintenanceSpec struct { // Indicates whether maintenance mode should be enabled for the app. Enabled bool `json:"enabled,omitempty"` - // Indicates whether the app should be archived. Setting this to true implies that enabled is set to true. - // Note that this feature is currently in closed beta. + // Indicates whether the app should be archived. Setting this to true implies that enabled is set to true. Note that this feature is currently in closed beta. Archive bool `json:"archive,omitempty"` } @@ -1004,6 +1003,7 @@ const ( DeploymentCauseDetailsDigitalOceanUserActionName_RollbackApp DeploymentCauseDetailsDigitalOceanUserActionName = "ROLLBACK_APP" DeploymentCauseDetailsDigitalOceanUserActionName_RevertAppRollback DeploymentCauseDetailsDigitalOceanUserActionName = "REVERT_APP_ROLLBACK" DeploymentCauseDetailsDigitalOceanUserActionName_UpgradeBuildpack DeploymentCauseDetailsDigitalOceanUserActionName = "UPGRADE_BUILDPACK" + DeploymentCauseDetailsDigitalOceanUserActionName_Restart DeploymentCauseDetailsDigitalOceanUserActionName = "RESTART" ) // AppDomain struct for AppDomain diff --git a/vendor/github.com/digitalocean/godo/apps.go b/vendor/github.com/digitalocean/godo/apps.go index ac792658e..97b0cbd73 100644 --- a/vendor/github.com/digitalocean/godo/apps.go +++ b/vendor/github.com/digitalocean/godo/apps.go @@ -35,11 +35,13 @@ type AppsService interface { Delete(ctx context.Context, appID string) (*Response, error) Propose(ctx context.Context, propose *AppProposeRequest) (*AppProposeResponse, *Response, error) + Restart(ctx context.Context, appID string, opts *AppRestartRequest) (*Deployment, *Response, error) GetDeployment(ctx context.Context, appID, deploymentID string) (*Deployment, *Response, error) ListDeployments(ctx context.Context, appID string, opts *ListOptions) ([]*Deployment, *Response, error) CreateDeployment(ctx context.Context, appID string, create ...*DeploymentCreateRequest) (*Deployment, *Response, error) GetLogs(ctx context.Context, appID, deploymentID, component string, logType AppLogType, follow bool, tailLines int) (*AppLogs, *Response, error) + GetExec(ctx context.Context, appID, deploymentID, component string) (*AppExec, *Response, error) ListRegions(ctx context.Context) ([]*AppRegion, *Response, error) @@ -77,6 +79,11 @@ type AppLogs struct { HistoricURLs []string `json:"historic_urls"` } +// AppExec represents the websocket URL used for sending/receiving console input and output. +type AppExec struct { + URL string `json:"url"` +} + // AppUpdateRequest represents a request to update an app. type AppUpdateRequest struct { Spec *AppSpec `json:"spec"` @@ -89,6 +96,11 @@ type DeploymentCreateRequest struct { ForceBuild bool `json:"force_build"` } +// AppRestartRequest represents a request to restart an app. +type AppRestartRequest struct { + Components []string `json:"components"` +} + // AlertDestinationUpdateRequest represents a request to update alert destinations. type AlertDestinationUpdateRequest struct { Emails []string `json:"emails"` @@ -279,6 +291,22 @@ func (s *AppsServiceOp) Propose(ctx context.Context, propose *AppProposeRequest) return res, resp, nil } +// Restart restarts an app. +func (s *AppsServiceOp) Restart(ctx context.Context, appID string, opts *AppRestartRequest) (*Deployment, *Response, error) { + path := fmt.Sprintf("%s/%s/restart", appsBasePath, appID) + + req, err := s.client.NewRequest(ctx, http.MethodPost, path, opts) + if err != nil { + return nil, nil, err + } + root := new(deploymentRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Deployment, resp, nil +} + // GetDeployment gets an app deployment. func (s *AppsServiceOp) GetDeployment(ctx context.Context, appID, deploymentID string) (*Deployment, *Response, error) { path := fmt.Sprintf("%s/%s/deployments/%s", appsBasePath, appID, deploymentID) @@ -368,6 +396,27 @@ func (s *AppsServiceOp) GetLogs(ctx context.Context, appID, deploymentID, compon return logs, resp, nil } +// GetExec retrieves the websocket URL used for sending/receiving console input and output. +func (s *AppsServiceOp) GetExec(ctx context.Context, appID, deploymentID, component string) (*AppExec, *Response, error) { + var url string + if deploymentID == "" { + url = fmt.Sprintf("%s/%s/components/%s/exec", appsBasePath, appID, component) + } else { + url = fmt.Sprintf("%s/%s/deployments/%s/components/%s/exec", appsBasePath, appID, deploymentID, component) + } + + req, err := s.client.NewRequest(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, nil, err + } + logs := new(AppExec) + resp, err := s.client.Do(ctx, req, logs) + if err != nil { + return nil, resp, err + } + return logs, resp, nil +} + // ListRegions lists all regions supported by App Platform. func (s *AppsServiceOp) ListRegions(ctx context.Context) ([]*AppRegion, *Response, error) { path := fmt.Sprintf("%s/regions", appsBasePath) diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go index 276fb4a6b..1217ef05e 100644 --- a/vendor/github.com/digitalocean/godo/databases.go +++ b/vendor/github.com/digitalocean/godo/databases.go @@ -299,19 +299,27 @@ type DatabaseBackupRestore struct { BackupCreatedAt string `json:"backup_created_at,omitempty"` } +// DatabaseCreateFirewallRule is a rule describing an inbound source to a database +type DatabaseCreateFirewallRule struct { + UUID string `json:"uuid"` + Type string `json:"type"` + Value string `json:"value"` +} + // DatabaseCreateRequest represents a request to create a database cluster type DatabaseCreateRequest struct { - Name string `json:"name,omitempty"` - EngineSlug string `json:"engine,omitempty"` - Version string `json:"version,omitempty"` - SizeSlug string `json:"size,omitempty"` - Region string `json:"region,omitempty"` - NumNodes int `json:"num_nodes,omitempty"` - PrivateNetworkUUID string `json:"private_network_uuid"` - Tags []string `json:"tags,omitempty"` - BackupRestore *DatabaseBackupRestore `json:"backup_restore,omitempty"` - ProjectID string `json:"project_id"` - StorageSizeMib uint64 `json:"storage_size_mib,omitempty"` + Name string `json:"name,omitempty"` + EngineSlug string `json:"engine,omitempty"` + Version string `json:"version,omitempty"` + SizeSlug string `json:"size,omitempty"` + Region string `json:"region,omitempty"` + NumNodes int `json:"num_nodes,omitempty"` + PrivateNetworkUUID string `json:"private_network_uuid"` + Tags []string `json:"tags,omitempty"` + BackupRestore *DatabaseBackupRestore `json:"backup_restore,omitempty"` + ProjectID string `json:"project_id"` + StorageSizeMib uint64 `json:"storage_size_mib,omitempty"` + Rules []*DatabaseCreateFirewallRule `json:"rules"` } // DatabaseResizeRequest can be used to initiate a database resize operation. @@ -589,6 +597,9 @@ type PostgreSQLConfig struct { BackupMinute *int `json:"backup_minute,omitempty"` WorkMem *int `json:"work_mem,omitempty"` TimeScaleDB *PostgreSQLTimeScaleDBConfig `json:"timescaledb,omitempty"` + SynchronousReplication *string `json:"synchronous_replication,omitempty"` + StatMonitorEnable *bool `json:"stat_monitor_enable,omitempty"` + MaxFailoverReplicationTimeLag *int64 `json:"max_failover_replication_time_lag,omitempty"` } // PostgreSQLBouncerConfig configuration @@ -653,6 +664,13 @@ type MySQLConfig struct { BackupHour *int `json:"backup_hour,omitempty"` BackupMinute *int `json:"backup_minute,omitempty"` BinlogRetentionPeriod *int `json:"binlog_retention_period,omitempty"` + InnodbChangeBufferMaxSize *int `json:"innodb_change_buffer_max_size,omitempty"` + InnodbFlushNeighbors *int `json:"innodb_flush_neighbors,omitempty"` + InnodbReadIoThreads *int `json:"innodb_read_io_threads,omitempty"` + InnodbThreadConcurrency *int `json:"innodb_thread_concurrency,omitempty"` + InnodbWriteIoThreads *int `json:"innodb_write_io_threads,omitempty"` + NetBufferLength *int `json:"net_buffer_length,omitempty"` + LogOutput *string `json:"log_output,omitempty"` } // MongoDBConfig holds advanced configurations for MongoDB database clusters. diff --git a/vendor/github.com/digitalocean/godo/droplet_actions.go b/vendor/github.com/digitalocean/godo/droplet_actions.go index 2e09d0c59..ed0f583c9 100644 --- a/vendor/github.com/digitalocean/godo/droplet_actions.go +++ b/vendor/github.com/digitalocean/godo/droplet_actions.go @@ -30,6 +30,8 @@ type DropletActionsService interface { SnapshotByTag(context.Context, string, string) ([]Action, *Response, error) EnableBackups(context.Context, int) (*Action, *Response, error) EnableBackupsByTag(context.Context, string) ([]Action, *Response, error) + EnableBackupsWithPolicy(context.Context, int, *DropletBackupPolicyRequest) (*Action, *Response, error) + ChangeBackupPolicy(context.Context, int, *DropletBackupPolicyRequest) (*Action, *Response, error) DisableBackups(context.Context, int) (*Action, *Response, error) DisableBackupsByTag(context.Context, string) ([]Action, *Response, error) PasswordReset(context.Context, int) (*Action, *Response, error) @@ -169,6 +171,42 @@ func (s *DropletActionsServiceOp) EnableBackupsByTag(ctx context.Context, tag st return s.doActionByTag(ctx, tag, request) } +// EnableBackupsWithPolicy enables droplet's backup with a backup policy applied. +func (s *DropletActionsServiceOp) EnableBackupsWithPolicy(ctx context.Context, id int, policy *DropletBackupPolicyRequest) (*Action, *Response, error) { + if policy == nil { + return nil, nil, NewArgError("policy", "policy can't be nil") + } + + policyMap := map[string]interface{}{ + "plan": policy.Plan, + "weekday": policy.Weekday, + } + if policy.Hour != nil { + policyMap["hour"] = policy.Hour + } + + request := &ActionRequest{"type": "enable_backups", "backup_policy": policyMap} + return s.doAction(ctx, id, request) +} + +// ChangeBackupPolicy updates a backup policy when backups are enabled. +func (s *DropletActionsServiceOp) ChangeBackupPolicy(ctx context.Context, id int, policy *DropletBackupPolicyRequest) (*Action, *Response, error) { + if policy == nil { + return nil, nil, NewArgError("policy", "policy can't be nil") + } + + policyMap := map[string]interface{}{ + "plan": policy.Plan, + "weekday": policy.Weekday, + } + if policy.Hour != nil { + policyMap["hour"] = policy.Hour + } + + request := &ActionRequest{"type": "change_backup_policy", "backup_policy": policyMap} + return s.doAction(ctx, id, request) +} + // DisableBackups disables backups for a Droplet. func (s *DropletActionsServiceOp) DisableBackups(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "disable_backups"} diff --git a/vendor/github.com/digitalocean/godo/droplets.go b/vendor/github.com/digitalocean/godo/droplets.go index 1ed09ec8c..2ddd7d6b7 100644 --- a/vendor/github.com/digitalocean/godo/droplets.go +++ b/vendor/github.com/digitalocean/godo/droplets.go @@ -30,6 +30,9 @@ type DropletsService interface { Backups(context.Context, int, *ListOptions) ([]Image, *Response, error) Actions(context.Context, int, *ListOptions) ([]Action, *Response, error) Neighbors(context.Context, int) ([]Droplet, *Response, error) + GetBackupPolicy(context.Context, int) (*DropletBackupPolicy, *Response, error) + ListBackupPolicies(context.Context, *ListOptions) (map[int]*DropletBackupPolicy, *Response, error) + ListSupportedBackupPolicies(context.Context) ([]*SupportedBackupPolicy, *Response, error) } // DropletsServiceOp handles communication with the Droplet related methods of the @@ -218,37 +221,46 @@ func (d DropletCreateSSHKey) MarshalJSON() ([]byte, error) { // DropletCreateRequest represents a request to create a Droplet. type DropletCreateRequest struct { - Name string `json:"name"` - Region string `json:"region"` - Size string `json:"size"` - Image DropletCreateImage `json:"image"` - SSHKeys []DropletCreateSSHKey `json:"ssh_keys"` - Backups bool `json:"backups"` - IPv6 bool `json:"ipv6"` - PrivateNetworking bool `json:"private_networking"` - Monitoring bool `json:"monitoring"` - UserData string `json:"user_data,omitempty"` - Volumes []DropletCreateVolume `json:"volumes,omitempty"` - Tags []string `json:"tags"` - VPCUUID string `json:"vpc_uuid,omitempty"` - WithDropletAgent *bool `json:"with_droplet_agent,omitempty"` + Name string `json:"name"` + Region string `json:"region"` + Size string `json:"size"` + Image DropletCreateImage `json:"image"` + SSHKeys []DropletCreateSSHKey `json:"ssh_keys"` + Backups bool `json:"backups"` + IPv6 bool `json:"ipv6"` + PrivateNetworking bool `json:"private_networking"` + Monitoring bool `json:"monitoring"` + UserData string `json:"user_data,omitempty"` + Volumes []DropletCreateVolume `json:"volumes,omitempty"` + Tags []string `json:"tags"` + VPCUUID string `json:"vpc_uuid,omitempty"` + WithDropletAgent *bool `json:"with_droplet_agent,omitempty"` + BackupPolicy *DropletBackupPolicyRequest `json:"backup_policy,omitempty"` } // DropletMultiCreateRequest is a request to create multiple Droplets. type DropletMultiCreateRequest struct { - Names []string `json:"names"` - Region string `json:"region"` - Size string `json:"size"` - Image DropletCreateImage `json:"image"` - SSHKeys []DropletCreateSSHKey `json:"ssh_keys"` - Backups bool `json:"backups"` - IPv6 bool `json:"ipv6"` - PrivateNetworking bool `json:"private_networking"` - Monitoring bool `json:"monitoring"` - UserData string `json:"user_data,omitempty"` - Tags []string `json:"tags"` - VPCUUID string `json:"vpc_uuid,omitempty"` - WithDropletAgent *bool `json:"with_droplet_agent,omitempty"` + Names []string `json:"names"` + Region string `json:"region"` + Size string `json:"size"` + Image DropletCreateImage `json:"image"` + SSHKeys []DropletCreateSSHKey `json:"ssh_keys"` + Backups bool `json:"backups"` + IPv6 bool `json:"ipv6"` + PrivateNetworking bool `json:"private_networking"` + Monitoring bool `json:"monitoring"` + UserData string `json:"user_data,omitempty"` + Tags []string `json:"tags"` + VPCUUID string `json:"vpc_uuid,omitempty"` + WithDropletAgent *bool `json:"with_droplet_agent,omitempty"` + BackupPolicy *DropletBackupPolicyRequest `json:"backup_policy,omitempty"` +} + +// DropletBackupPolicyRequest defines the backup policy when creating a Droplet. +type DropletBackupPolicyRequest struct { + Plan string `json:"plan,omitempty"` + Weekday string `json:"weekday,omitempty"` + Hour *int `json:"hour,omitempty"` } func (d DropletCreateRequest) String() string { @@ -618,3 +630,109 @@ func (s *DropletsServiceOp) dropletActionStatus(ctx context.Context, uri string) return action.Status, nil } + +// DropletBackupPolicy defines the information about a droplet's backup policy. +type DropletBackupPolicy struct { + DropletID int `json:"droplet_id,omitempty"` + BackupEnabled bool `json:"backup_enabled,omitempty"` + BackupPolicy *DropletBackupPolicyConfig `json:"backup_policy,omitempty"` + NextBackupWindow *BackupWindow `json:"next_backup_window,omitempty"` +} + +// DropletBackupPolicyConfig defines the backup policy for a Droplet. +type DropletBackupPolicyConfig struct { + Plan string `json:"plan,omitempty"` + Weekday string `json:"weekday,omitempty"` + Hour int `json:"hour,omitempty"` + WindowLengthHours int `json:"window_length_hours,omitempty"` + RetentionPeriodDays int `json:"retention_period_days,omitempty"` +} + +// dropletBackupPolicyRoot represents a DropletBackupPolicy root +type dropletBackupPolicyRoot struct { + DropletBackupPolicy *DropletBackupPolicy `json:"policy,omitempty"` +} + +type dropletBackupPoliciesRoot struct { + DropletBackupPolicies map[int]*DropletBackupPolicy `json:"policies,omitempty"` + Links *Links `json:"links,omitempty"` + Meta *Meta `json:"meta"` +} + +// Get individual droplet backup policy. +func (s *DropletsServiceOp) GetBackupPolicy(ctx context.Context, dropletID int) (*DropletBackupPolicy, *Response, error) { + if dropletID < 1 { + return nil, nil, NewArgError("dropletID", "cannot be less than 1") + } + + path := fmt.Sprintf("%s/%d/backups/policy", dropletBasePath, dropletID) + + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(dropletBackupPolicyRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root.DropletBackupPolicy, resp, err +} + +// List all droplet backup policies. +func (s *DropletsServiceOp) ListBackupPolicies(ctx context.Context, opt *ListOptions) (map[int]*DropletBackupPolicy, *Response, error) { + path := fmt.Sprintf("%s/backups/policies", dropletBasePath) + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(dropletBackupPoliciesRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + if m := root.Meta; m != nil { + resp.Meta = m + } + + return root.DropletBackupPolicies, resp, nil +} + +type SupportedBackupPolicy struct { + Name string `json:"name,omitempty"` + PossibleWindowStarts []int `json:"possible_window_starts,omitempty"` + WindowLengthHours int `json:"window_length_hours,omitempty"` + RetentionPeriodDays int `json:"retention_period_days,omitempty"` + PossibleDays []string `json:"possible_days,omitempty"` +} + +type dropletSupportedBackupPoliciesRoot struct { + SupportedBackupPolicies []*SupportedBackupPolicy `json:"supported_policies,omitempty"` +} + +// List supported droplet backup policies. +func (s *DropletsServiceOp) ListSupportedBackupPolicies(ctx context.Context) ([]*SupportedBackupPolicy, *Response, error) { + path := fmt.Sprintf("%s/backups/supported_policies", dropletBasePath) + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(dropletSupportedBackupPoliciesRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root.SupportedBackupPolicies, resp, nil +} diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index edf0f6d46..62498fc44 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -21,7 +21,7 @@ import ( ) const ( - libraryVersion = "1.128.0" + libraryVersion = "1.131.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" @@ -54,42 +54,45 @@ type Client struct { ratemtx sync.Mutex // Services used for communicating with the API - Account AccountService - Actions ActionsService - Apps AppsService - Balance BalanceService - BillingHistory BillingHistoryService - CDNs CDNService - Certificates CertificatesService - Databases DatabasesService - Domains DomainsService - Droplets DropletsService - DropletActions DropletActionsService - DropletAutoscale DropletAutoscaleService - Firewalls FirewallsService - FloatingIPs FloatingIPsService - FloatingIPActions FloatingIPActionsService - Functions FunctionsService - Images ImagesService - ImageActions ImageActionsService - Invoices InvoicesService - Keys KeysService - Kubernetes KubernetesService - LoadBalancers LoadBalancersService - Monitoring MonitoringService - OneClick OneClickService - Projects ProjectsService - Regions RegionsService - Registry RegistryService - ReservedIPs ReservedIPsService - ReservedIPActions ReservedIPActionsService - Sizes SizesService - Snapshots SnapshotsService - Storage StorageService - StorageActions StorageActionsService - Tags TagsService - UptimeChecks UptimeChecksService - VPCs VPCsService + Account AccountService + Actions ActionsService + Apps AppsService + Balance BalanceService + BillingHistory BillingHistoryService + CDNs CDNService + Certificates CertificatesService + Databases DatabasesService + Domains DomainsService + Droplets DropletsService + DropletActions DropletActionsService + DropletAutoscale DropletAutoscaleService + Firewalls FirewallsService + FloatingIPs FloatingIPsService + FloatingIPActions FloatingIPActionsService + Functions FunctionsService + Images ImagesService + ImageActions ImageActionsService + Invoices InvoicesService + Keys KeysService + Kubernetes KubernetesService + LoadBalancers LoadBalancersService + Monitoring MonitoringService + OneClick OneClickService + Projects ProjectsService + Regions RegionsService + Registry RegistryService + Registries RegistriesService + ReservedIPs ReservedIPsService + ReservedIPV6s ReservedIPV6sService + ReservedIPActions ReservedIPActionsService + ReservedIPV6Actions ReservedIPV6ActionsService + Sizes SizesService + Snapshots SnapshotsService + Storage StorageService + StorageActions StorageActionsService + Tags TagsService + UptimeChecks UptimeChecksService + VPCs VPCsService // Optional function called after every successful request made to the DO APIs onRequestCompleted RequestCompletionCallback @@ -292,8 +295,11 @@ func NewClient(httpClient *http.Client) *Client { c.Projects = &ProjectsServiceOp{client: c} c.Regions = &RegionsServiceOp{client: c} c.Registry = &RegistryServiceOp{client: c} + c.Registries = &RegistriesServiceOp{client: c} c.ReservedIPs = &ReservedIPsServiceOp{client: c} + c.ReservedIPV6s = &ReservedIPV6sServiceOp{client: c} c.ReservedIPActions = &ReservedIPActionsServiceOp{client: c} + c.ReservedIPV6Actions = &ReservedIPV6ActionsServiceOp{client: c} c.Sizes = &SizesServiceOp{client: c} c.Snapshots = &SnapshotsServiceOp{client: c} c.Storage = &StorageServiceOp{client: c} diff --git a/vendor/github.com/digitalocean/godo/load_balancers.go b/vendor/github.com/digitalocean/godo/load_balancers.go index a24952b71..a12729dd6 100644 --- a/vendor/github.com/digitalocean/godo/load_balancers.go +++ b/vendor/github.com/digitalocean/godo/load_balancers.go @@ -45,6 +45,7 @@ type LoadBalancer struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` IP string `json:"ip,omitempty"` + IPv6 string `json:"ipv6,omitempty"` // SizeSlug is mutually exclusive with SizeUnit. Only one should be specified SizeSlug string `json:"size,omitempty"` // SizeUnit is mutually exclusive with SizeSlug. Only one should be specified diff --git a/vendor/github.com/digitalocean/godo/registry.go b/vendor/github.com/digitalocean/godo/registry.go index b0c243281..e64822682 100644 --- a/vendor/github.com/digitalocean/godo/registry.go +++ b/vendor/github.com/digitalocean/godo/registry.go @@ -14,6 +14,9 @@ const ( registryPath = "/v2/registry" // RegistryServer is the hostname of the DigitalOcean registry service RegistryServer = "registry.digitalocean.com" + + // Multi-registry Open Beta API constants + registriesPath = "/v2/registries" ) // RegistryService is an interface for interfacing with the Registry endpoints @@ -240,6 +243,19 @@ type RegistryValidateNameRequest struct { Name string `json:"name"` } +// Multi-registry Open Beta API structs + +type registriesRoot struct { + Registries []*Registry `json:"registries,omitempty"` + TotalStorageUsageBytes uint64 `json:"total_storage_usage_bytes,omitempty"` +} + +// RegistriesCreateRequest represents a request to create a secondary registry. +type RegistriesCreateRequest struct { + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` +} + // Get retrieves the details of a Registry. func (svc *RegistryServiceOp) Get(ctx context.Context) (*Registry, *Response, error) { req, err := svc.client.NewRequest(ctx, http.MethodGet, registryPath, nil) @@ -610,3 +626,107 @@ func (svc *RegistryServiceOp) ValidateName(ctx context.Context, request *Registr } return resp, nil } + +// RegistriesService is an interface for interfacing with the new multiple-registry beta endpoints +// of the DigitalOcean API. +// +// We are creating a separate Service in alignment with the new /v2/registries endpoints. +type RegistriesService interface { + Get(context.Context, string) (*Registry, *Response, error) + List(context.Context) ([]*Registry, *Response, error) + Create(context.Context, *RegistriesCreateRequest) (*Registry, *Response, error) + Delete(context.Context, string) (*Response, error) + DockerCredentials(context.Context, string, *RegistryDockerCredentialsRequest) (*DockerCredentials, *Response, error) +} + +var _ RegistriesService = &RegistriesServiceOp{} + +// RegistriesServiceOp handles communication with the multiple-registry beta methods. +type RegistriesServiceOp struct { + client *Client +} + +// Get returns the details of a named Registry. +func (svc *RegistriesServiceOp) Get(ctx context.Context, registry string) (*Registry, *Response, error) { + path := fmt.Sprintf("%s/%s", registriesPath, registry) + req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + root := new(registryRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Registry, resp, nil +} + +// List returns a list of the named Registries. +func (svc *RegistriesServiceOp) List(ctx context.Context) ([]*Registry, *Response, error) { + req, err := svc.client.NewRequest(ctx, http.MethodGet, registriesPath, nil) + if err != nil { + return nil, nil, err + } + root := new(registriesRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Registries, resp, nil +} + +// Create creates a named Registry. +func (svc *RegistriesServiceOp) Create(ctx context.Context, create *RegistriesCreateRequest) (*Registry, *Response, error) { + req, err := svc.client.NewRequest(ctx, http.MethodPost, registriesPath, create) + if err != nil { + return nil, nil, err + } + root := new(registryRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Registry, resp, nil +} + +// Delete deletes a named Registry. There is no way to recover a Registry once it has +// been destroyed. +func (svc *RegistriesServiceOp) Delete(ctx context.Context, registry string) (*Response, error) { + path := fmt.Sprintf("%s/%s", registriesPath, registry) + req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + resp, err := svc.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// DockerCredentials retrieves a Docker config file containing named Registry's credentials. +func (svc *RegistriesServiceOp) DockerCredentials(ctx context.Context, registry string, request *RegistryDockerCredentialsRequest) (*DockerCredentials, *Response, error) { + path := fmt.Sprintf("%s/%s/%s", registriesPath, registry, "docker-credentials") + req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + q := req.URL.Query() + q.Add("read_write", strconv.FormatBool(request.ReadWrite)) + if request.ExpirySeconds != nil { + q.Add("expiry_seconds", strconv.Itoa(*request.ExpirySeconds)) + } + req.URL.RawQuery = q.Encode() + + var buf bytes.Buffer + resp, err := svc.client.Do(ctx, req, &buf) + if err != nil { + return nil, resp, err + } + + dc := &DockerCredentials{ + DockerConfigJSON: buf.Bytes(), + } + return dc, resp, nil +} diff --git a/vendor/github.com/digitalocean/godo/reserved_ipv6.go b/vendor/github.com/digitalocean/godo/reserved_ipv6.go new file mode 100644 index 000000000..aa2656359 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/reserved_ipv6.go @@ -0,0 +1,132 @@ +package godo + +import ( + "context" + "fmt" + "net/http" + "time" +) + +const resourceV6Type = "ReservedIPv6" +const reservedIPV6sBasePath = "v2/reserved_ipv6" + +// ReservedIPV6sService is an interface for interfacing with the reserved IPV6s +// endpoints of the Digital Ocean API. +type ReservedIPV6sService interface { + List(context.Context, *ListOptions) ([]ReservedIPV6, *Response, error) + Get(context.Context, string) (*ReservedIPV6, *Response, error) + Create(context.Context, *ReservedIPV6CreateRequest) (*ReservedIPV6, *Response, error) + Delete(context.Context, string) (*Response, error) +} + +// ReservedIPV6sServiceOp handles communication with the reserved IPs related methods of the +// DigitalOcean API. +type ReservedIPV6sServiceOp struct { + client *Client +} + +var _ ReservedIPV6sService = (*ReservedIPV6sServiceOp)(nil) + +// ReservedIPV6 represents a Digital Ocean reserved IP. +type ReservedIPV6 struct { + RegionSlug string `json:"region_slug"` + IP string `json:"ip"` + ReservedAt time.Time `json:"reserved_at"` + Droplet *Droplet `json:"droplet,omitempty"` +} + +func (f ReservedIPV6) String() string { + return Stringify(f) +} + +// URN returns the reserved IP in a valid DO API URN form. +func (f ReservedIPV6) URN() string { + return ToURN(resourceV6Type, f.IP) +} + +type reservedIPV6sRoot struct { + ReservedIPs []ReservedIPV6 `json:"reserved_ips"` + Links *Links `json:"links"` + Meta *Meta `json:"meta"` +} + +// ReservedIPV6CreateRequest represents a request to reserve a reserved IP. +type ReservedIPV6CreateRequest struct { + Region string `json:"region_slug,omitempty"` +} + +// List all reserved IPV6s. +func (r *ReservedIPV6sServiceOp) List(ctx context.Context, opt *ListOptions) ([]ReservedIPV6, *Response, error) { + path := reservedIPV6sBasePath + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + + req, err := r.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(reservedIPV6sRoot) + resp, err := r.client.Do(ctx, req, root) + if err != nil { + return nil, nil, err + } + if l := root.Links; l != nil { + resp.Links = l + } + if m := root.Meta; m != nil { + resp.Meta = m + } + + return root.ReservedIPs, resp, err +} + +// Get an individual reserved IPv6. +func (r *ReservedIPV6sServiceOp) Get(ctx context.Context, ip string) (*ReservedIPV6, *Response, error) { + path := fmt.Sprintf("%s/%s", reservedIPV6sBasePath, ip) + + req, err := r.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(ReservedIPV6) + resp, err := r.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root, resp, err +} + +// Create a new IPv6 +func (r *ReservedIPV6sServiceOp) Create(ctx context.Context, reserveRequest *ReservedIPV6CreateRequest) (*ReservedIPV6, *Response, error) { + path := reservedIPV6sBasePath + + req, err := r.client.NewRequest(ctx, http.MethodPost, path, reserveRequest) + if err != nil { + return nil, nil, err + } + + root := new(ReservedIPV6) + resp, err := r.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root, resp, err +} + +// Delete a reserved IPv6. +func (r *ReservedIPV6sServiceOp) Delete(ctx context.Context, ip string) (*Response, error) { + path := fmt.Sprintf("%s/%s", reservedIPV6sBasePath, ip) + + req, err := r.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + + return r.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/digitalocean/godo/reserved_ipv6_actions.go b/vendor/github.com/digitalocean/godo/reserved_ipv6_actions.go new file mode 100644 index 000000000..dd14bc58b --- /dev/null +++ b/vendor/github.com/digitalocean/godo/reserved_ipv6_actions.go @@ -0,0 +1,57 @@ +package godo + +import ( + "context" + "fmt" + "net/http" +) + +// ReservedIPActionsService is an interface for interfacing with the +// reserved IPs actions endpoints of the Digital Ocean API. +// See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Reserved-IP-Actions +type ReservedIPV6ActionsService interface { + Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error) + Unassign(ctx context.Context, ip string) (*Action, *Response, error) +} + +// ReservedIPActionsServiceOp handles communication with the reserved IPs +// action related methods of the DigitalOcean API. +type ReservedIPV6ActionsServiceOp struct { + client *Client +} + +// Assign a reserved IP to a droplet. +func (s *ReservedIPV6ActionsServiceOp) Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error) { + request := &ActionRequest{ + "type": "assign", + "droplet_id": dropletID, + } + return s.doV6Action(ctx, ip, request) +} + +// Unassign a rerserved IP from the droplet it is currently assigned to. +func (s *ReservedIPV6ActionsServiceOp) Unassign(ctx context.Context, ip string) (*Action, *Response, error) { + request := &ActionRequest{"type": "unassign"} + return s.doV6Action(ctx, ip, request) +} + +func (s *ReservedIPV6ActionsServiceOp) doV6Action(ctx context.Context, ip string, request *ActionRequest) (*Action, *Response, error) { + path := reservedIPV6ActionPath(ip) + + req, err := s.client.NewRequest(ctx, http.MethodPost, path, request) + if err != nil { + return nil, nil, err + } + + root := new(actionRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root.Event, resp, err +} + +func reservedIPV6ActionPath(ip string) string { + return fmt.Sprintf("%s/%s/actions", reservedIPV6sBasePath, ip) +} diff --git a/vendor/github.com/digitalocean/godo/strings.go b/vendor/github.com/digitalocean/godo/strings.go index f92893ed2..5a258131e 100644 --- a/vendor/github.com/digitalocean/godo/strings.go +++ b/vendor/github.com/digitalocean/godo/strings.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "reflect" + "sort" "strings" ) @@ -46,6 +47,8 @@ func stringifyValue(w io.Writer, val reflect.Value) { return case reflect.Struct: stringifyStruct(w, v) + case reflect.Map: + stringifyMap(w, v) default: if v.CanInterface() { fmt.Fprint(w, v.Interface()) @@ -66,6 +69,27 @@ func stringifySlice(w io.Writer, v reflect.Value) { _, _ = w.Write([]byte{']'}) } +func stringifyMap(w io.Writer, v reflect.Value) { + _, _ = w.Write([]byte("map[")) + + // Sort the keys so that the output is stable + keys := v.MapKeys() + sort.Slice(keys, func(i, j int) bool { + return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j]) + }) + + for i, key := range keys { + stringifyValue(w, key) + _, _ = w.Write([]byte{':'}) + stringifyValue(w, v.MapIndex(key)) + if i < len(keys)-1 { + _, _ = w.Write([]byte(", ")) + } + } + + _, _ = w.Write([]byte("]")) +} + func stringifyStruct(w io.Writer, v reflect.Value) { if v.Type().Name() != "" { _, _ = w.Write([]byte(v.Type().String())) diff --git a/vendor/modules.txt b/vendor/modules.txt index 04b805a87..5852bf786 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -58,7 +58,7 @@ github.com/aws/aws-sdk-go/service/sts/stsiface # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/digitalocean/godo v1.128.1-0.20241025145008-2654a9d1e887 +# github.com/digitalocean/godo v1.131.0 ## explicit; go 1.22 github.com/digitalocean/godo github.com/digitalocean/godo/metrics