From cd82a3b826e1a9f7d00db85526020076b8ff17e1 Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Tue, 13 Dec 2022 13:14:13 -0600 Subject: [PATCH 1/6] Pin godo dep to master for development --- go.mod | 2 +- go.sum | 2 + .../github.com/digitalocean/godo/functions.go | 130 +++++++++++++++++- vendor/github.com/digitalocean/godo/godo.go | 44 +++--- .../digitalocean/godo/load_balancers.go | 33 +++++ .../digitalocean/godo/monitoring.go | 22 +-- vendor/modules.txt | 2 +- 7 files changed, 200 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 13040ffbf..c958ff84d 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.91.1 + github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2 github.com/hashicorp/awspolicyequivalence v1.5.0 github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.3.0 diff --git a/go.sum b/go.sum index 2f13b888f..f5a0d01d4 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,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.91.1 h1:1o30VOCu1aC6488qBd0SkQiBeAZ35RSTvLwCA1pQMhc= github.com/digitalocean/godo v1.91.1/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= +github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2 h1:/sjXBM7IYbeXH0/2FhYjNJZsLiCIr3HwW6bS8taKX9A= +github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/vendor/github.com/digitalocean/godo/functions.go b/vendor/github.com/digitalocean/godo/functions.go index b3829367a..61c80778a 100644 --- a/vendor/github.com/digitalocean/godo/functions.go +++ b/vendor/github.com/digitalocean/godo/functions.go @@ -8,8 +8,9 @@ import ( ) const ( - functionsBasePath = "/v2/functions/namespaces" - functionsNamespacePath = functionsBasePath + "/%s" + functionsBasePath = "/v2/functions/namespaces" + functionsNamespacePath = functionsBasePath + "/%s" + functionsTriggerBasePath = functionsNamespacePath + "/triggers" ) type FunctionsService interface { @@ -17,6 +18,12 @@ type FunctionsService interface { GetNamespace(context.Context, string) (*FunctionsNamespace, *Response, error) CreateNamespace(context.Context, *FunctionsNamespaceCreateRequest) (*FunctionsNamespace, *Response, error) DeleteNamespace(context.Context, string) (*Response, error) + + ListTriggers(context.Context, string) ([]FunctionsTrigger, *Response, error) + GetTrigger(context.Context, string, string) (*FunctionsTrigger, *Response, error) + CreateTrigger(context.Context, string, *FunctionsTriggerCreateRequest) (*FunctionsTrigger, *Response, error) + UpdateTrigger(context.Context, string, string, *FunctionsTriggerUpdateRequest) (*FunctionsTrigger, *Response, error) + DeleteTrigger(context.Context, string, string) (*Response, error) } type FunctionsServiceOp struct { @@ -49,6 +56,49 @@ type FunctionsNamespaceCreateRequest struct { Region string `json:"region"` } +type triggersRoot struct { + Triggers []FunctionsTrigger `json:"triggers,omitempty"` +} + +type triggerRoot struct { + Trigger *FunctionsTrigger `json:"trigger,omitempty"` +} + +type FunctionsTrigger struct { + Namespace string `json:"namespace,omitempty"` + Function string `json:"function,omitempty"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + IsEnabled bool `json:"is_enabled"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + ScheduledDetails *TriggerScheduledDetails `json:"scheduled_details,omitempty"` + ScheduledRuns *TriggerScheduledRuns `json:"scheduled_runs,omitempty"` +} + +type TriggerScheduledDetails struct { + Cron string `json:"cron,omitempty"` + Body map[string]interface{} `json:"body,omitempty"` +} + +type TriggerScheduledRuns struct { + LastRunAt time.Time `json:"last_run_at,omitempty"` + NextRunAt time.Time `json:"next_run_at,omitempty"` +} + +type FunctionsTriggerCreateRequest struct { + Name string `json:"name"` + Type string `json:"type"` + Function string `json:"function"` + IsEnabled bool `json:"is_enabled"` + ScheduledDetails *TriggerScheduledDetails `json:"scheduled_details,omitempty"` +} + +type FunctionsTriggerUpdateRequest struct { + IsEnabled *bool `json:"is_enabled,omitempty"` + ScheduledDetails *TriggerScheduledDetails `json:"scheduled_details,omitempty"` +} + // Gets a list of namespaces func (s *FunctionsServiceOp) ListNamespaces(ctx context.Context) ([]FunctionsNamespace, *Response, error) { req, err := s.client.NewRequest(ctx, http.MethodGet, functionsBasePath, nil) @@ -108,3 +158,79 @@ func (s *FunctionsServiceOp) DeleteNamespace(ctx context.Context, namespace stri } return resp, nil } + +// ListTriggers gets a list of triggers +func (s *FunctionsServiceOp) ListTriggers(ctx context.Context, namespace string) ([]FunctionsTrigger, *Response, error) { + path := fmt.Sprintf(functionsTriggerBasePath, namespace) + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + root := new(triggersRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Triggers, resp, nil +} + +// GetTrigger gets a single trigger +func (s *FunctionsServiceOp) GetTrigger(ctx context.Context, namespace string, trigger string) (*FunctionsTrigger, *Response, error) { + path := fmt.Sprintf(functionsTriggerBasePath+"/%s", namespace, trigger) + req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + root := new(triggerRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Trigger, resp, nil +} + +// CreateTrigger creates a trigger +func (s *FunctionsServiceOp) CreateTrigger(ctx context.Context, namespace string, opts *FunctionsTriggerCreateRequest) (*FunctionsTrigger, *Response, error) { + path := fmt.Sprintf(functionsTriggerBasePath, namespace) + req, err := s.client.NewRequest(ctx, http.MethodPost, path, opts) + if err != nil { + return nil, nil, err + } + root := new(triggerRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Trigger, resp, nil +} + +// UpdateTrigger updates a trigger +func (s *FunctionsServiceOp) UpdateTrigger(ctx context.Context, namespace string, trigger string, opts *FunctionsTriggerUpdateRequest) (*FunctionsTrigger, *Response, error) { + path := fmt.Sprintf(functionsTriggerBasePath+"/%s", namespace, trigger) + req, err := s.client.NewRequest(ctx, http.MethodPut, path, opts) + + if err != nil { + return nil, nil, err + } + root := new(triggerRoot) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Trigger, resp, nil +} + +// DeleteTrigger deletes a trigger +func (s *FunctionsServiceOp) DeleteTrigger(ctx context.Context, namespace string, trigger string) (*Response, error) { + path := fmt.Sprintf(functionsTriggerBasePath+"/%s", namespace, trigger) + req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) + + if err != nil { + return nil, err + } + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 7c15e92ff..21570146a 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -54,34 +54,34 @@ type Client struct { Balance BalanceService BillingHistory BillingHistoryService CDNs CDNService + Certificates CertificatesService + Databases DatabasesService Domains DomainsService Droplets DropletsService DropletActions DropletActionsService + 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 - Sizes SizesService - FloatingIPs FloatingIPsService - FloatingIPActions FloatingIPActionsService + Registry RegistryService ReservedIPs ReservedIPsService ReservedIPActions ReservedIPActionsService + Sizes SizesService Snapshots SnapshotsService Storage StorageService StorageActions StorageActionsService Tags TagsService - LoadBalancers LoadBalancersService - Certificates CertificatesService - Firewalls FirewallsService - Projects ProjectsService - Kubernetes KubernetesService - Registry RegistryService - Databases DatabasesService VPCs VPCsService - OneClick OneClickService - Monitoring MonitoringService - Functions FunctionsService // Optional function called after every successful request made to the DO APIs onRequestCompleted RequestCompletionCallback @@ -216,6 +216,7 @@ func NewClient(httpClient *http.Client) *Client { baseURL, _ := url.Parse(defaultBaseURL) c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent} + c.Account = &AccountServiceOp{client: c} c.Actions = &ActionsServiceOp{client: c} c.Apps = &AppsServiceOp{client: c} @@ -223,33 +224,34 @@ func NewClient(httpClient *http.Client) *Client { c.BillingHistory = &BillingHistoryServiceOp{client: c} c.CDNs = &CDNServiceOp{client: c} c.Certificates = &CertificatesServiceOp{client: c} + c.Databases = &DatabasesServiceOp{client: c} c.Domains = &DomainsServiceOp{client: c} c.Droplets = &DropletsServiceOp{client: c} c.DropletActions = &DropletActionsServiceOp{client: c} c.Firewalls = &FirewallsServiceOp{client: c} c.FloatingIPs = &FloatingIPsServiceOp{client: c} c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c} - c.ReservedIPs = &ReservedIPsServiceOp{client: c} - c.ReservedIPActions = &ReservedIPActionsServiceOp{client: c} + c.Functions = &FunctionsServiceOp{client: c} c.Images = &ImagesServiceOp{client: c} c.ImageActions = &ImageActionsServiceOp{client: c} c.Invoices = &InvoicesServiceOp{client: c} c.Keys = &KeysServiceOp{client: c} + c.Kubernetes = &KubernetesServiceOp{client: c} c.LoadBalancers = &LoadBalancersServiceOp{client: c} + c.Monitoring = &MonitoringServiceOp{client: c} + c.OneClick = &OneClickServiceOp{client: c} c.Projects = &ProjectsServiceOp{client: c} c.Regions = &RegionsServiceOp{client: c} + c.Registry = &RegistryServiceOp{client: c} + c.ReservedIPs = &ReservedIPsServiceOp{client: c} + c.ReservedIPActions = &ReservedIPActionsServiceOp{client: c} c.Sizes = &SizesServiceOp{client: c} c.Snapshots = &SnapshotsServiceOp{client: c} c.Storage = &StorageServiceOp{client: c} c.StorageActions = &StorageActionsServiceOp{client: c} c.Tags = &TagsServiceOp{client: c} - c.Kubernetes = &KubernetesServiceOp{client: c} - c.Registry = &RegistryServiceOp{client: c} - c.Databases = &DatabasesServiceOp{client: c} c.VPCs = &VPCsServiceOp{client: c} - c.OneClick = &OneClickServiceOp{client: c} - c.Monitoring = &MonitoringServiceOp{client: c} - c.Functions = &FunctionsServiceOp{client: c} + c.headers = make(map[string]string) return c diff --git a/vendor/github.com/digitalocean/godo/load_balancers.go b/vendor/github.com/digitalocean/godo/load_balancers.go index 1466d5239..6a9a70efe 100644 --- a/vendor/github.com/digitalocean/godo/load_balancers.go +++ b/vendor/github.com/digitalocean/godo/load_balancers.go @@ -53,6 +53,7 @@ type LoadBalancer struct { ValidateOnly bool `json:"validate_only,omitempty"` ProjectID string `json:"project_id,omitempty"` HTTPIdleTimeoutSeconds *uint64 `json:"http_idle_timeout_seconds,omitempty"` + Firewall *LBFirewall `json:"firewall,omitempty"` } // String creates a human-readable description of a LoadBalancer. @@ -103,6 +104,10 @@ func (l LoadBalancer) AsRequest() *LoadBalancerRequest { r.Region = l.Region.Slug } + if l.Firewall != nil { + r.Firewall = l.Firewall.deepCopy() + } + return &r } @@ -149,6 +154,33 @@ func (s StickySessions) String() string { return Stringify(s) } +// LBFirewall holds the allow and deny rules for a loadbalancer's firewall. +// Currently, allow and deny rules support cidrs and ips. +// Please use the helper methods (IPSourceFirewall/CIDRSourceFirewall) to format the allow/deny rules. +type LBFirewall struct { + Allow []string `json:"allow,omitempty"` + Deny []string `json:"deny,omitempty"` +} + +func (lbf *LBFirewall) deepCopy() *LBFirewall { + return &LBFirewall{ + Allow: append([]string(nil), lbf.Allow...), + Deny: append([]string(nil), lbf.Deny...), + } +} + +// IPSourceFirewall takes an IP (string) and returns a formatted ip source firewall rule +func IPSourceFirewall(ip string) string { return fmt.Sprintf("ip:%s", ip) } + +// CIDRSourceFirewall takes a CIDR notation IP address and prefix length string +// like "192.0.2.0/24" and returns a formatted cidr source firewall rule +func CIDRSourceFirewall(cidr string) string { return fmt.Sprintf("cidr:%s", cidr) } + +// String creates a human-readable description of an LBFirewall instance. +func (f LBFirewall) String() string { + return Stringify(f) +} + // LoadBalancerRequest represents the configuration to be applied to an existing or a new load balancer. type LoadBalancerRequest struct { Name string `json:"name,omitempty"` @@ -172,6 +204,7 @@ type LoadBalancerRequest struct { ValidateOnly bool `json:"validate_only,omitempty"` ProjectID string `json:"project_id,omitempty"` HTTPIdleTimeoutSeconds *uint64 `json:"http_idle_timeout_seconds,omitempty"` + Firewall *LBFirewall `json:"firewall,omitempty"` } // String creates a human-readable description of a LoadBalancerRequest. diff --git a/vendor/github.com/digitalocean/godo/monitoring.go b/vendor/github.com/digitalocean/godo/monitoring.go index 5934c8757..937bb8d91 100644 --- a/vendor/github.com/digitalocean/godo/monitoring.go +++ b/vendor/github.com/digitalocean/godo/monitoring.go @@ -27,16 +27,18 @@ const ( DropletFiveMinuteLoadAverage = "v1/insights/droplet/load_5" DropletFifteenMinuteLoadAverage = "v1/insights/droplet/load_15" - LoadBalancerCPUUtilizationPercent = "v1/insights/lbaas/avg_cpu_utilization_percent" - LoadBalancerConnectionUtilizationPercent = "v1/insights/lbaas/connection_utilization_percent" - LoadBalancerDropletHealth = "v1/insights/lbaas/droplet_health" - LoadBalancerTLSUtilizationPercent = "v1/insights/lbaas/tls_connections_per_second_utilization_percent" - LoadBalancerIncreaseInHTTPErrorRatePercentage = "v1/insights/lbaas/increase_in_http_error_rate_percentage" - LoadBalancerIncreaseInHTTPErrorRateCount = "v1/insights/lbaas/increase_in_http_error_rate_count" - LoadBalancerHighHttpResponseTime = "v1/insights/lbaas/high_http_request_response_time" - LoadBalancerHighHttpResponseTime50P = "v1/insights/lbaas/high_http_request_response_time_50p" - LoadBalancerHighHttpResponseTime95P = "v1/insights/lbaas/high_http_request_response_time_95p" - LoadBalancerHighHttpResponseTime99P = "v1/insights/lbaas/high_http_request_response_time_99p" + LoadBalancerCPUUtilizationPercent = "v1/insights/lbaas/avg_cpu_utilization_percent" + LoadBalancerConnectionUtilizationPercent = "v1/insights/lbaas/connection_utilization_percent" + LoadBalancerDropletHealth = "v1/insights/lbaas/droplet_health" + LoadBalancerTLSUtilizationPercent = "v1/insights/lbaas/tls_connections_per_second_utilization_percent" + LoadBalancerIncreaseInHTTPErrorRatePercentage5xx = "v1/insights/lbaas/increase_in_http_error_rate_percentage_5xx" + LoadBalancerIncreaseInHTTPErrorRatePercentage4xx = "v1/insights/lbaas/increase_in_http_error_rate_percentage_4xx" + LoadBalancerIncreaseInHTTPErrorRateCount5xx = "v1/insights/lbaas/increase_in_http_error_rate_count_5xx" + LoadBalancerIncreaseInHTTPErrorRateCount4xx = "v1/insights/lbaas/increase_in_http_error_rate_count_4xx" + LoadBalancerHighHttpResponseTime = "v1/insights/lbaas/high_http_request_response_time" + LoadBalancerHighHttpResponseTime50P = "v1/insights/lbaas/high_http_request_response_time_50p" + LoadBalancerHighHttpResponseTime95P = "v1/insights/lbaas/high_http_request_response_time_95p" + LoadBalancerHighHttpResponseTime99P = "v1/insights/lbaas/high_http_request_response_time_99p" DbaasFifteenMinuteLoadAverage = "v1/dbaas/alerts/load_15_alerts" DbaasMemoryUtilizationPercent = "v1/dbaas/alerts/memory_utilization_alerts" diff --git a/vendor/modules.txt b/vendor/modules.txt index 41531c311..51f7a8910 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -64,7 +64,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.91.1 +# github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2 ## explicit; go 1.18 github.com/digitalocean/godo github.com/digitalocean/godo/metrics From cd3ebb40c5e8b941dc158851869524f9cee69fd4 Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Tue, 13 Dec 2022 13:15:22 -0600 Subject: [PATCH 2/6] Patch monitor alerts after godo upgrade (LB http alerts were split into 4xx and 5xx alerts) --- digitalocean/resource_digitalocean_monitor_alert.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/digitalocean/resource_digitalocean_monitor_alert.go b/digitalocean/resource_digitalocean_monitor_alert.go index 919342770..c66bd4e0b 100644 --- a/digitalocean/resource_digitalocean_monitor_alert.go +++ b/digitalocean/resource_digitalocean_monitor_alert.go @@ -46,8 +46,10 @@ func resourceDigitalOceanMonitorAlert() *schema.Resource { godo.LoadBalancerConnectionUtilizationPercent, godo.LoadBalancerDropletHealth, godo.LoadBalancerTLSUtilizationPercent, - godo.LoadBalancerIncreaseInHTTPErrorRatePercentage, - godo.LoadBalancerIncreaseInHTTPErrorRateCount, + godo.LoadBalancerIncreaseInHTTPErrorRatePercentage4xx, + godo.LoadBalancerIncreaseInHTTPErrorRatePercentage5xx, + godo.LoadBalancerIncreaseInHTTPErrorRateCount4xx, + godo.LoadBalancerIncreaseInHTTPErrorRateCount5xx, godo.LoadBalancerHighHttpResponseTime, godo.LoadBalancerHighHttpResponseTime50P, godo.LoadBalancerHighHttpResponseTime95P, From e5f0519d155177bf0e5d976dc5bdca07c499c725 Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Tue, 13 Dec 2022 13:16:30 -0600 Subject: [PATCH 3/6] Add Firewall support to Load Balancer datasource & resource --- .../datasource_digitalocean_loadbalancer.go | 26 ++++++++++++- digitalocean/loadbalancer.go | 39 +++++++++++++++++++ .../resource_digitalocean_loadbalancer.go | 31 +++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/digitalocean/datasource_digitalocean_loadbalancer.go b/digitalocean/datasource_digitalocean_loadbalancer.go index bf45b83bd..55a908e68 100644 --- a/digitalocean/datasource_digitalocean_loadbalancer.go +++ b/digitalocean/datasource_digitalocean_loadbalancer.go @@ -220,12 +220,32 @@ func dataSourceDigitalOceanLoadbalancer() *schema.Resource { Computed: true, Description: " Specifies the idle timeout for HTTPS connections on the load balancer.", }, - "project_id": { Type: schema.TypeString, Computed: true, Description: "The ID of the project that the load balancer is associated with.", }, + "firewall": { + Type: schema.TypeList, + Computed: true, + Description: "the firewall rules for allowing/denying traffic to the load balancer", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "the rules for ALLOWING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", + }, + "deny": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "the rules for DENYING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", + }, + }, + }, + }, }, } } @@ -324,6 +344,10 @@ func dataSourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resou return diag.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err) } + if err := d.Set("firewall", flattenLBFirewall(foundLoadbalancer.Firewall)); err != nil { + return diag.Errorf("[DEBUG] Error setting Load Balancer firewall - error: %#v", err) + } + return nil } diff --git a/digitalocean/loadbalancer.go b/digitalocean/loadbalancer.go index 77062e677..8309ed51c 100644 --- a/digitalocean/loadbalancer.go +++ b/digitalocean/loadbalancer.go @@ -41,6 +41,22 @@ func expandStickySessions(config []interface{}) *godo.StickySessions { return stickySession } +func expandLBFirewall(config []interface{}) *godo.LBFirewall { + firewallConfig := config[0].(map[string]interface{}) + + firewall := &godo.LBFirewall{} + + if v, ok := firewallConfig["allow"]; ok { + firewall.Allow = v.([]string) + } + + if v, ok := firewallConfig["deny"]; ok { + firewall.Deny = v.([]string) + } + + return firewall +} + func expandHealthCheck(config []interface{}) *godo.HealthCheck { healthcheckConfig := config[0].(map[string]interface{}) @@ -187,6 +203,29 @@ func flattenStickySessions(session *godo.StickySessions) []map[string]interface{ return result } +func flattenLBFirewall(firewall *godo.LBFirewall) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + if firewall != nil { + r := make(map[string]interface{}) + // TODO: Unsure if nested rules requiring flattening? (jrolheiser) + r["allow"] = flattenFirewallRules((*firewall).Allow) + r["deny"] = flattenFirewallRules((*firewall).Deny) + + result = append(result, r) + } + + return result +} + +func flattenFirewallRules(rules []string) *schema.Set { + flatSet := schema.NewSet(schema.HashString, []interface{}{}) + for _, v := range rules { + flatSet.Add(v) + } + return flatSet +} + func flattenForwardingRules(client *godo.Client, rules []godo.ForwardingRule) ([]map[string]interface{}, error) { result := make([]map[string]interface{}, 0, 1) diff --git a/digitalocean/resource_digitalocean_loadbalancer.go b/digitalocean/resource_digitalocean_loadbalancer.go index 17f178887..3f520b5d4 100644 --- a/digitalocean/resource_digitalocean_loadbalancer.go +++ b/digitalocean/resource_digitalocean_loadbalancer.go @@ -377,6 +377,29 @@ func resourceDigitalOceanLoadBalancerV0() *schema.Resource { Optional: true, Computed: true, }, + + "firewall": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "the rules for ALLOWING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", + }, + "deny": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "the rules for DENYING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", + }, + }, + }, + }, }, } } @@ -459,6 +482,10 @@ func buildLoadBalancerRequest(client *godo.Client, d *schema.ResourceData) (*god opts.StickySessions = expandStickySessions(v.([]interface{})) } + if v, ok := d.GetOk("firewall"); ok { + opts.Firewall = expandLBFirewall(v.([]interface{})) + } + if v, ok := d.GetOk("vpc_uuid"); ok { opts.VPCUUID = v.(string) } @@ -556,6 +583,10 @@ func resourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resourc return diag.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err) } + if err := d.Set("firewall", flattenLBFirewall(loadbalancer.Firewall)); err != nil { + return diag.Errorf("[DEBUG] Error setting Load Balancer firewall - error: %#v", err) + } + return nil } From 1dc8f0774ed28928a80b5298f363215c50bd11f7 Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Wed, 14 Dec 2022 12:00:55 -0600 Subject: [PATCH 4/6] Bump godo to v1.92.0 --- go.mod | 2 +- go.sum | 2 ++ .../github.com/digitalocean/godo/CHANGELOG.md | 8 ++++++ vendor/github.com/digitalocean/godo/godo.go | 2 +- vendor/github.com/digitalocean/godo/keys.go | 26 +++++++++---------- vendor/modules.txt | 2 +- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index c958ff84d..35b5caa1e 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.91.2-0.20221209163753-d8d6ec42f6f2 + github.com/digitalocean/godo v1.92.0 github.com/hashicorp/awspolicyequivalence v1.5.0 github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.3.0 diff --git a/go.sum b/go.sum index f5a0d01d4..0a1da83b0 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/digitalocean/godo v1.91.1 h1:1o30VOCu1aC6488qBd0SkQiBeAZ35RSTvLwCA1pQ github.com/digitalocean/godo v1.91.1/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2 h1:/sjXBM7IYbeXH0/2FhYjNJZsLiCIr3HwW6bS8taKX9A= github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= +github.com/digitalocean/godo v1.92.0 h1:eK9DgdLcjozZbywjh/9k2NF++ttbcidASOoCGRu48yQ= +github.com/digitalocean/godo v1.92.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 803ba787d..04da396cd 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## [v1.92.0] - 2022-12-08 + +- #589 - @wez470 - load-balancers: Minor doc fixup +- #585 - @StephenVarela - Add firewall support for load balancers +- #587 - @StephenVarela - Support new http alerts for load balancers +- #586 - @andrewsomething - godo.go: Sort service lists. +- #583 - @ddebarros - Adds support for functions trigger API + ## [v1.91.1] - 2022-11-23 - #582 - @StephenVarela - Load Balancers: Support new endpoints for http alerts diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 21570146a..354a1d516 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.91.1" + libraryVersion = "1.92.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" diff --git a/vendor/github.com/digitalocean/godo/keys.go b/vendor/github.com/digitalocean/godo/keys.go index d50946c23..cd0bd29d5 100644 --- a/vendor/github.com/digitalocean/godo/keys.go +++ b/vendor/github.com/digitalocean/godo/keys.go @@ -8,7 +8,7 @@ import ( const keysBasePath = "v2/account/keys" -// KeysService is an interface for interfacing with the keys +// KeysService is an interface for interfacing with the SSH keys // endpoints of the DigitalOcean API // See: https://docs.digitalocean.com/reference/api/api-reference/#tag/SSH-Keys type KeysService interface { @@ -22,7 +22,7 @@ type KeysService interface { DeleteByFingerprint(context.Context, string) (*Response, error) } -// KeysServiceOp handles communication with key related method of the +// KeysServiceOp handles communication with SSH key related method of the // DigitalOcean API. type KeysServiceOp struct { client *Client @@ -38,7 +38,7 @@ type Key struct { PublicKey string `json:"public_key,omitempty"` } -// KeyUpdateRequest represents a request to update a DigitalOcean key. +// KeyUpdateRequest represents a request to update an SSH key stored in a DigitalOcean account. type KeyUpdateRequest struct { Name string `json:"name"` } @@ -57,13 +57,13 @@ func (s Key) String() string { return Stringify(s) } -// KeyCreateRequest represents a request to create a new key. +// KeyCreateRequest represents a request to create a new SSH key. type KeyCreateRequest struct { Name string `json:"name"` PublicKey string `json:"public_key"` } -// List all keys +// List all SSH keys func (s *KeysServiceOp) List(ctx context.Context, opt *ListOptions) ([]Key, *Response, error) { path := keysBasePath path, err := addOptions(path, opt) @@ -107,7 +107,7 @@ func (s *KeysServiceOp) get(ctx context.Context, path string) (*Key, *Response, return root.SSHKey, resp, err } -// GetByID gets a Key by id +// GetByID gets an SSH key by its ID func (s *KeysServiceOp) GetByID(ctx context.Context, keyID int) (*Key, *Response, error) { if keyID < 1 { return nil, nil, NewArgError("keyID", "cannot be less than 1") @@ -117,7 +117,7 @@ func (s *KeysServiceOp) GetByID(ctx context.Context, keyID int) (*Key, *Response return s.get(ctx, path) } -// GetByFingerprint gets a Key by fingerprint +// GetByFingerprint gets an SSH key by its fingerprint func (s *KeysServiceOp) GetByFingerprint(ctx context.Context, fingerprint string) (*Key, *Response, error) { if len(fingerprint) < 1 { return nil, nil, NewArgError("fingerprint", "cannot not be empty") @@ -127,7 +127,7 @@ func (s *KeysServiceOp) GetByFingerprint(ctx context.Context, fingerprint string return s.get(ctx, path) } -// Create a key using a KeyCreateRequest +// Create an SSH key using a KeyCreateRequest func (s *KeysServiceOp) Create(ctx context.Context, createRequest *KeyCreateRequest) (*Key, *Response, error) { if createRequest == nil { return nil, nil, NewArgError("createRequest", "cannot be nil") @@ -147,7 +147,7 @@ func (s *KeysServiceOp) Create(ctx context.Context, createRequest *KeyCreateRequ return root.SSHKey, resp, err } -// UpdateByID updates a key name by ID. +// UpdateByID updates an SSH key name by ID. func (s *KeysServiceOp) UpdateByID(ctx context.Context, keyID int, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { if keyID < 1 { return nil, nil, NewArgError("keyID", "cannot be less than 1") @@ -172,7 +172,7 @@ func (s *KeysServiceOp) UpdateByID(ctx context.Context, keyID int, updateRequest return root.SSHKey, resp, err } -// UpdateByFingerprint updates a key name by fingerprint. +// UpdateByFingerprint updates an SSH key name by fingerprint. func (s *KeysServiceOp) UpdateByFingerprint(ctx context.Context, fingerprint string, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { if len(fingerprint) < 1 { return nil, nil, NewArgError("fingerprint", "cannot be empty") @@ -197,7 +197,7 @@ func (s *KeysServiceOp) UpdateByFingerprint(ctx context.Context, fingerprint str return root.SSHKey, resp, err } -// Delete key using a path +// Delete an SSH key using a path func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, error) { req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil) if err != nil { @@ -209,7 +209,7 @@ func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, err return resp, err } -// DeleteByID deletes a key by its id +// DeleteByID deletes an SSH key by its id func (s *KeysServiceOp) DeleteByID(ctx context.Context, keyID int) (*Response, error) { if keyID < 1 { return nil, NewArgError("keyID", "cannot be less than 1") @@ -219,7 +219,7 @@ func (s *KeysServiceOp) DeleteByID(ctx context.Context, keyID int) (*Response, e return s.delete(ctx, path) } -// DeleteByFingerprint deletes a key by its fingerprint +// DeleteByFingerprint deletes an SSH key by its fingerprint func (s *KeysServiceOp) DeleteByFingerprint(ctx context.Context, fingerprint string) (*Response, error) { if len(fingerprint) < 1 { return nil, NewArgError("fingerprint", "cannot be empty") diff --git a/vendor/modules.txt b/vendor/modules.txt index 51f7a8910..d7598357b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -64,7 +64,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.91.2-0.20221209163753-d8d6ec42f6f2 +# github.com/digitalocean/godo v1.92.0 ## explicit; go 1.18 github.com/digitalocean/godo github.com/digitalocean/godo/metrics From 7155a01694b484a92bd999be4c5b4b56255ef926 Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Wed, 14 Dec 2022 14:13:08 -0600 Subject: [PATCH 5/6] Run `go mod tidy`, update monitor alert doc --- docs/resources/monitor_alert.md | 3 ++- go.sum | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/resources/monitor_alert.md b/docs/resources/monitor_alert.md index e9b4d0ba6..1a5362971 100644 --- a/docs/resources/monitor_alert.md +++ b/docs/resources/monitor_alert.md @@ -58,7 +58,8 @@ The following arguments are supported: `v1/insights/droplet/private_outbound_bandwidth`, `v1/insights/droplet/private_inbound_bandwidth`, `v1/insights/lbaas/avg_cpu_utilization_percent`, `v1/insights/lbaas/connection_utilization_percent`, `v1/insights/lbaas/droplet_health`, `v1/insights/lbaas/tls_connections_per_second_utilization_percent`, - `v1/insights/lbaas/increase_in_http_error_rate_percentage`, `v1/insights/lbaas/increase_in_http_error_rate_count`, + `v1/insights/lbaas/increase_in_http_error_rate_percentage_5xx`, `v1/insights/lbaas/increase_in_http_error_rate_percentage_4xx`, + `v1/insights/lbaas/increase_in_http_error_rate_count_5xx`, `v1/insights/lbaas/increase_in_http_error_rate_count_4xx`, `v1/insights/lbaas/high_http_request_response_time`, `v1/insights/lbaas/high_http_request_response_time_50p`, `v1/insights/lbaas/high_http_request_response_time_95p`, `v1/insights/lbaas/high_http_request_response_time_99p`, `v1/dbaas/alerts/load_15_alerts`, `v1/dbaas/alerts/cpu_alerts`, `v1/dbaas/alerts/memory_utilization_alerts`, or diff --git a/go.sum b/go.sum index 0a1da83b0..22fa7fc78 100644 --- a/go.sum +++ b/go.sum @@ -81,10 +81,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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.91.1 h1:1o30VOCu1aC6488qBd0SkQiBeAZ35RSTvLwCA1pQMhc= -github.com/digitalocean/godo v1.91.1/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= -github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2 h1:/sjXBM7IYbeXH0/2FhYjNJZsLiCIr3HwW6bS8taKX9A= -github.com/digitalocean/godo v1.91.2-0.20221209163753-d8d6ec42f6f2/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= github.com/digitalocean/godo v1.92.0 h1:eK9DgdLcjozZbywjh/9k2NF++ttbcidASOoCGRu48yQ= github.com/digitalocean/godo v1.92.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= From 93deeb16271281ff3bf8339acf4d202157ef9b3e Mon Sep 17 00:00:00 2001 From: jrolheiser Date: Tue, 20 Dec 2022 11:39:45 -0600 Subject: [PATCH 6/6] Add regression test and documentation for setting LB firewalls. --- digitalocean/loadbalancer.go | 25 ++++---- .../resource_digitalocean_loadbalancer.go | 12 ++-- ...resource_digitalocean_loadbalancer_test.go | 62 +++++++++++++++++++ docs/resources/loadbalancer.md | 9 ++- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/digitalocean/loadbalancer.go b/digitalocean/loadbalancer.go index 8309ed51c..e14e98cf2 100644 --- a/digitalocean/loadbalancer.go +++ b/digitalocean/loadbalancer.go @@ -47,11 +47,19 @@ func expandLBFirewall(config []interface{}) *godo.LBFirewall { firewall := &godo.LBFirewall{} if v, ok := firewallConfig["allow"]; ok { - firewall.Allow = v.([]string) + allows := make([]string, 0, len(v.([]interface{}))) + for _, val := range v.([]interface{}) { + allows = append(allows, val.(string)) + } + firewall.Allow = allows } if v, ok := firewallConfig["deny"]; ok { - firewall.Deny = v.([]string) + denies := make([]string, 0, len(v.([]interface{}))) + for _, val := range v.([]interface{}) { + denies = append(denies, val.(string)) + } + firewall.Deny = denies } return firewall @@ -208,9 +216,8 @@ func flattenLBFirewall(firewall *godo.LBFirewall) []map[string]interface{} { if firewall != nil { r := make(map[string]interface{}) - // TODO: Unsure if nested rules requiring flattening? (jrolheiser) - r["allow"] = flattenFirewallRules((*firewall).Allow) - r["deny"] = flattenFirewallRules((*firewall).Deny) + r["allow"] = (*firewall).Allow + r["deny"] = (*firewall).Deny result = append(result, r) } @@ -218,14 +225,6 @@ func flattenLBFirewall(firewall *godo.LBFirewall) []map[string]interface{} { return result } -func flattenFirewallRules(rules []string) *schema.Set { - flatSet := schema.NewSet(schema.HashString, []interface{}{}) - for _, v := range rules { - flatSet.Add(v) - } - return flatSet -} - func flattenForwardingRules(client *godo.Client, rules []godo.ForwardingRule) ([]map[string]interface{}, error) { result := make([]map[string]interface{}, 0, 1) diff --git a/digitalocean/resource_digitalocean_loadbalancer.go b/digitalocean/resource_digitalocean_loadbalancer.go index 3f520b5d4..1f04abf07 100644 --- a/digitalocean/resource_digitalocean_loadbalancer.go +++ b/digitalocean/resource_digitalocean_loadbalancer.go @@ -379,22 +379,22 @@ func resourceDigitalOceanLoadBalancerV0() *schema.Resource { }, "firewall": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "allow": { - Type: schema.TypeSet, + Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, + Optional: true, Description: "the rules for ALLOWING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", }, "deny": { - Type: schema.TypeSet, + Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, + Optional: true, Description: "the rules for DENYING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')", }, }, @@ -483,7 +483,7 @@ func buildLoadBalancerRequest(client *godo.Client, d *schema.ResourceData) (*god } if v, ok := d.GetOk("firewall"); ok { - opts.Firewall = expandLBFirewall(v.([]interface{})) + opts.Firewall = expandLBFirewall(v.(*schema.Set).List()) } if v, ok := d.GetOk("vpc_uuid"); ok { diff --git a/digitalocean/resource_digitalocean_loadbalancer_test.go b/digitalocean/resource_digitalocean_loadbalancer_test.go index 4a8f85f79..510935736 100644 --- a/digitalocean/resource_digitalocean_loadbalancer_test.go +++ b/digitalocean/resource_digitalocean_loadbalancer_test.go @@ -735,6 +735,37 @@ func TestAccDigitalOceanLoadbalancer_WithVPC(t *testing.T) { }) } +func TestAccDigitalOceanLoadbalancer_Firewall(t *testing.T) { + var loadbalancer godo.LoadBalancer + lbName := randomTestName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckDigitalOceanLoadbalancerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckDigitalOceanLoadbalancerConfig_Firewall(lbName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "name", lbName), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "firewall.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "firewall.0.deny.0", "cidr:1.2.0.0/16"), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "firewall.0.deny.1", "ip:2.3.4.5"), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "firewall.0.allow.0", "ip:1.2.3.4"), + resource.TestCheckResourceAttr( + "digitalocean_loadbalancer.foobar", "firewall.0.allow.1", "cidr:2.3.4.0/24"), + ), + }, + }, + }) +} + func testAccCheckDigitalOceanLoadbalancerDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*CombinedConfig).godoClient() @@ -1122,3 +1153,34 @@ resource "digitalocean_loadbalancer" "foobar" { droplet_ids = [digitalocean_droplet.foobar.id] }`, randomTestName(), randomTestName(), name) } + +func testAccCheckDigitalOceanLoadbalancerConfig_Firewall(name string) string { + return fmt.Sprintf(` +resource "digitalocean_droplet" "foobar" { + name = "%s" + size = "s-1vcpu-1gb" + image = "ubuntu-22-04-x64" + region = "nyc3" +} + +resource "digitalocean_loadbalancer" "foobar" { + name = "%s" + region = "nyc3" + size = "lb-small" + + forwarding_rule { + entry_port = 80 + entry_protocol = "http" + + target_port = 80 + target_protocol = "http" + } + + firewall { + deny = ["cidr:1.2.0.0/16", "ip:2.3.4.5"] + allow = ["ip:1.2.3.4", "cidr:2.3.4.0/24"] + } + + droplet_ids = [digitalocean_droplet.foobar.id] +}`, randomTestName(), name) +} diff --git a/docs/resources/loadbalancer.md b/docs/resources/loadbalancer.md index 0236e58a8..ef0f45f2e 100644 --- a/docs/resources/loadbalancer.md +++ b/docs/resources/loadbalancer.md @@ -115,6 +115,7 @@ the backend service. Default value is `false`. * `vpc_uuid` - (Optional) The ID of the VPC where the load balancer will be located. * `droplet_ids` (Optional) - A list of the IDs of each droplet to be attached to the Load Balancer. * `droplet_tag` (Optional) - The name of a Droplet tag corresponding to Droplets to be assigned to the Load Balancer. +* `firewall` (Optional) - A block containing rules for allowing/denying traffic to the Load Balancer. The `firewall` block is documented below. Only 1 firewall is allowed. `forwarding_rule` supports the following: @@ -137,11 +138,17 @@ the backend service. Default value is `false`. * `protocol` - (Required) The protocol used for health checks sent to the backend Droplets. The possible values are `http`, `https` or `tcp`. * `port` - (Optional) An integer representing the port on the backend Droplets on which the health check will attempt a connection. * `path` - (Optional) The path on the backend Droplets to which the Load Balancer instance will send a request. -* `check_interval_seconds` - (Optional) The number of seconds between between two consecutive health checks. If not specified, the default value is `10`. +* `check_interval_seconds` - (Optional) The number of seconds between two consecutive health checks. If not specified, the default value is `10`. * `response_timeout_seconds` - (Optional) The number of seconds the Load Balancer instance will wait for a response until marking a health check as failed. If not specified, the default value is `5`. * `unhealthy_threshold` - (Optional) The number of times a health check must fail for a backend Droplet to be marked "unhealthy" and be removed from the pool. If not specified, the default value is `3`. * `healthy_threshold` - (Optional) The number of times a health check must pass for a backend Droplet to be marked "healthy" and be re-added to the pool. If not specified, the default value is `5`. +`firewall` supports the following: + +* `deny` - (Optional) A list of strings describing deny rules. Must be colon delimited strings of the form `{type}:{source}` +* `allow` - (Optional) A list of strings describing allow rules. Must be colon delimited strings of the form `{type}:{source}` +* Ex. `deny = ["cidr:1.2.0.0/16", "ip:2.3.4.5"]` or `allow = ["ip:1.2.3.4", "cidr:2.3.4.0/24"]` + ## Attributes Reference