diff --git a/.changelog/3381.txt b/.changelog/3381.txt new file mode 100644 index 00000000000..ada647bbdd4 --- /dev/null +++ b/.changelog/3381.txt @@ -0,0 +1,15 @@ +```release-note:new-resource +`google_compute_region_url_map` is now GA +``` +```release-note:new-resource +`google_compute_region_target_http_proxy` is now GA +``` +```release-note:new-resource +`google_compute_region_target_https_proxy` is now GA +``` +```release-note:enhancement +compute: Promoted the following `google_compute_backend_service` fields to GA: `circuit_breakers`, `consistent_hash`, `custom_request_headers`, `locality_lb_policy`, `outlier_detection` +``` +```release-note:enhancement +compute: Promoted the following `google_compute_region_backend_service` fields to GA: `affinity_cookie_ttl_sec`,`circuit_breakers`, `consistent_hash`, `failover_policy`, `locality_lb_policy`, `outlier_detection`, `log_config`, `failover` +``` diff --git a/google/provider.go b/google/provider.go index 17594cb0ab2..ed88db4d305 100644 --- a/google/provider.go +++ b/google/provider.go @@ -538,9 +538,9 @@ func Provider() terraform.ResourceProvider { return provider } -// Generated resources: 122 +// Generated resources: 125 // Generated IAM resources: 51 -// Total generated resources: 173 +// Total generated resources: 176 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -613,6 +613,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_node_template": resourceComputeNodeTemplate(), "google_compute_region_autoscaler": resourceComputeRegionAutoscaler(), "google_compute_region_disk": resourceComputeRegionDisk(), + "google_compute_region_url_map": resourceComputeRegionUrlMap(), "google_compute_region_health_check": resourceComputeRegionHealthCheck(), "google_compute_resource_policy": resourceComputeResourcePolicy(), "google_compute_route": resourceComputeRoute(), @@ -630,6 +631,8 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_subnetwork_iam_policy": ResourceIamPolicy(ComputeSubnetworkIamSchema, ComputeSubnetworkIamUpdaterProducer, ComputeSubnetworkIdParseFunc), "google_compute_target_http_proxy": resourceComputeTargetHttpProxy(), "google_compute_target_https_proxy": resourceComputeTargetHttpsProxy(), + "google_compute_region_target_http_proxy": resourceComputeRegionTargetHttpProxy(), + "google_compute_region_target_https_proxy": resourceComputeRegionTargetHttpsProxy(), "google_compute_target_instance": resourceComputeTargetInstance(), "google_compute_target_ssl_proxy": resourceComputeTargetSslProxy(), "google_compute_target_tcp_proxy": resourceComputeTargetTcpProxy(), diff --git a/google/resource_compute_backend_service.go b/google/resource_compute_backend_service.go index 1219d9a8600..24674d20637 100644 --- a/google/resource_compute_backend_service.go +++ b/google/resource_compute_backend_service.go @@ -299,6 +299,58 @@ responses will not be altered.`, }, }, }, + "circuit_breakers": { + Type: schema.TypeList, + Optional: true, + Description: `Settings controlling the volume of connections to a backend service. This field +is applicable only when the load_balancing_scheme is set to INTERNAL_SELF_MANAGED.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_connections": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of connections to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_pending_requests": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of pending requests to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_requests": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of parallel requests to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_requests_per_connection": { + Type: schema.TypeInt, + Optional: true, + Description: `Maximum requests for a single backend connection. This parameter +is respected by both the HTTP/1.1 and HTTP/2 implementations. If +not specified, there is no limit. Setting this parameter to 1 +will effectively disable keep alive.`, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_retries": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of parallel retries to the backend cluster. +Defaults to 3.`, + Default: 3, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + }, + }, + }, "connection_draining_timeout_sec": { Type: schema.TypeInt, Optional: true, @@ -307,6 +359,103 @@ connections, but still work to finish started).`, Default: 300, }, + "consistent_hash": { + Type: schema.TypeList, + Optional: true, + Description: `Consistent Hash-based load balancing can be used to provide soft session +affinity based on HTTP headers, cookies or other properties. This load balancing +policy is applicable only for HTTP connections. The affinity to a particular +destination host will be lost when one or more hosts are added/removed from the +destination service. This field specifies parameters that control consistent +hashing. This field only applies if the load_balancing_scheme is set to +INTERNAL_SELF_MANAGED. This field is only applicable when locality_lb_policy is +set to MAGLEV or RING_HASH.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_cookie": { + Type: schema.TypeList, + Optional: true, + Description: `Hash is based on HTTP Cookie. This field describes a HTTP cookie +that will be used as the hash key for the consistent hash load +balancer. If the cookie is not present, it will be generated. +This field is applicable if the sessionAffinity is set to HTTP_COOKIE.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Description: `Name of the cookie.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Description: `Path to set for the cookie.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + "ttl": { + Type: schema.TypeList, + Optional: true, + Description: `Lifetime of the cookie.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. +Must be from 0 to 315,576,000,000 inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond +resolution. Durations less than one second are represented +with a 0 seconds field and a positive nanos field. Must +be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + }, + }, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + "http_header_name": { + Type: schema.TypeString, + Optional: true, + Description: `The hash based on the value of the specified header field. +This field is applicable if the sessionAffinity is set to HEADER_FIELD.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + "minimum_ring_size": { + Type: schema.TypeInt, + Optional: true, + Description: `The minimum number of virtual nodes to use for the hash ring. +Larger ring sizes result in more granular load +distributions. If the number of hosts in the load balancing pool +is larger than the ring size, each host will be assigned a single +virtual node. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + }, + }, + }, + "custom_request_headers": { + Type: schema.TypeSet, + Optional: true, + Description: `Headers that the HTTP/S load balancer should add to proxied +requests.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, "description": { Type: schema.TypeString, Optional: true, @@ -354,6 +503,216 @@ external load balancing. A backend service created for one type of load balancing cannot be used with the other. Default value: "EXTERNAL" Possible values: ["EXTERNAL", "INTERNAL_SELF_MANAGED"]`, Default: "EXTERNAL", }, + "locality_lb_policy": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV", ""}, false), + Description: `The load balancing algorithm used within the scope of the locality. +The possible values are - + +ROUND_ROBIN - This is a simple policy in which each healthy backend + is selected in round robin order. + +LEAST_REQUEST - An O(1) algorithm which selects two random healthy + hosts and picks the host which has fewer active requests. + +RING_HASH - The ring/modulo hash load balancer implements consistent + hashing to backends. The algorithm has the property that the + addition/removal of a host from a set of N hosts only affects + 1/N of the requests. + +RANDOM - The load balancer selects a random healthy host. + +ORIGINAL_DESTINATION - Backend host is selected based on the client + connection metadata, i.e., connections are opened + to the same address as the destination address of + the incoming connection before the connection + was redirected to the load balancer. + +MAGLEV - used as a drop in replacement for the ring hash load balancer. + Maglev is not as stable as ring hash but has faster table lookup + build times and host selection times. For more information about + Maglev, refer to https://ai.google/research/pubs/pub44824 + +This field is applicable only when the load_balancing_scheme is set to +INTERNAL_SELF_MANAGED. Possible values: ["ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV"]`, + }, + "log_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `This field denotes the logging options for the load balancer traffic served by this backend service. +If logging is enabled, logs will be exported to Stackdriver.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether to enable logging for the load balancer traffic served by this backend service.`, + AtLeastOneOf: []string{"log_config.0.enable", "log_config.0.sample_rate"}, + }, + "sample_rate": { + Type: schema.TypeFloat, + Optional: true, + Description: `This field can only be specified if logging is enabled for this backend service. The value of +the field must be in [0, 1]. This configures the sampling rate of requests to the load balancer +where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. +The default value is 1.0.`, + AtLeastOneOf: []string{"log_config.0.enable", "log_config.0.sample_rate"}, + }, + }, + }, + }, + "outlier_detection": { + Type: schema.TypeList, + Optional: true, + Description: `Settings controlling eviction of unhealthy hosts from the load balancing pool. +This field is applicable only when the load_balancing_scheme is set +to INTERNAL_SELF_MANAGED.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base_ejection_time": { + Type: schema.TypeList, + Optional: true, + Description: `The base time that a host is ejected for. The real time is equal to the base +time multiplied by the number of times the host has been ejected. Defaults to +30000ms or 30s.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "consecutive_errors": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of errors before a host is ejected from the connection pool. When the +backend host is accessed over HTTP, a 5xx return code qualifies as an error. +Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "consecutive_gateway_failure": { + Type: schema.TypeInt, + Optional: true, + Description: `The number of consecutive gateway failures (502, 503, 504 status or connection +errors that are mapped to one of those status codes) before a consecutive +gateway failure ejection occurs. Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_consecutive_errors": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through consecutive 5xx. This setting can be used to disable +ejection or to ramp it up slowly. Defaults to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_consecutive_gateway_failure": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through consecutive gateway failures. This setting can be +used to disable ejection or to ramp it up slowly. Defaults to 0.`, + Default: 0, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_success_rate": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through success rate statistics. This setting can be used to +disable ejection or to ramp it up slowly. Defaults to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "interval": { + Type: schema.TypeList, + Optional: true, + Description: `Time interval between ejection sweep analysis. This can result in both new +ejections as well as hosts being returned to service. Defaults to 10 seconds.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "max_ejection_percent": { + Type: schema.TypeInt, + Optional: true, + Description: `Maximum percentage of hosts in the load balancing pool for the backend service +that can be ejected. Defaults to 10%.`, + Default: 10, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_minimum_hosts": { + Type: schema.TypeInt, + Optional: true, + Description: `The number of hosts in a cluster that must have enough request volume to detect +success rate outliers. If the number of hosts is less than this setting, outlier +detection via success rate statistics is not performed for any host in the +cluster. Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_request_volume": { + Type: schema.TypeInt, + Optional: true, + Description: `The minimum number of total requests that must be collected in one interval (as +defined by the interval duration above) to include this host in success rate +based outlier detection. If the volume is lower than this setting, outlier +detection via success rate statistics is not performed for that host. Defaults +to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_stdev_factor": { + Type: schema.TypeInt, + Optional: true, + Description: `This factor is used to determine the ejection threshold for success rate outlier +ejection. The ejection threshold is the difference between the mean success +rate, and the product of this factor and the standard deviation of the mean +success rate: mean - (stdev * success_rate_stdev_factor). This factor is divided +by a thousand to get a double. That is, if the desired factor is 1.9, the +runtime value should be 1900. Defaults to 1900.`, + Default: 1900, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + }, + }, + }, "port_name": { Type: schema.TypeString, Computed: true, @@ -557,6 +916,18 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("backend"); !isEmptyValue(reflect.ValueOf(backendsProp)) && (ok || !reflect.DeepEqual(v, backendsProp)) { obj["backends"] = backendsProp } + circuitBreakersProp, err := expandComputeBackendServiceCircuitBreakers(d.Get("circuit_breakers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("circuit_breakers"); !isEmptyValue(reflect.ValueOf(circuitBreakersProp)) && (ok || !reflect.DeepEqual(v, circuitBreakersProp)) { + obj["circuitBreakers"] = circuitBreakersProp + } + consistentHashProp, err := expandComputeBackendServiceConsistentHash(d.Get("consistent_hash"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consistent_hash"); !isEmptyValue(reflect.ValueOf(consistentHashProp)) && (ok || !reflect.DeepEqual(v, consistentHashProp)) { + obj["consistentHash"] = consistentHashProp + } cdnPolicyProp, err := expandComputeBackendServiceCdnPolicy(d.Get("cdn_policy"), d, config) if err != nil { return err @@ -569,6 +940,12 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{ } else if !isEmptyValue(reflect.ValueOf(connectionDrainingProp)) { obj["connectionDraining"] = connectionDrainingProp } + customRequestHeadersProp, err := expandComputeBackendServiceCustomRequestHeaders(d.Get("custom_request_headers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("custom_request_headers"); !isEmptyValue(reflect.ValueOf(customRequestHeadersProp)) && (ok || !reflect.DeepEqual(v, customRequestHeadersProp)) { + obj["customRequestHeaders"] = customRequestHeadersProp + } fingerprintProp, err := expandComputeBackendServiceFingerprint(d.Get("fingerprint"), d, config) if err != nil { return err @@ -605,12 +982,24 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("load_balancing_scheme"); !isEmptyValue(reflect.ValueOf(loadBalancingSchemeProp)) && (ok || !reflect.DeepEqual(v, loadBalancingSchemeProp)) { obj["loadBalancingScheme"] = loadBalancingSchemeProp } + localityLbPolicyProp, err := expandComputeBackendServiceLocalityLbPolicy(d.Get("locality_lb_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("locality_lb_policy"); !isEmptyValue(reflect.ValueOf(localityLbPolicyProp)) && (ok || !reflect.DeepEqual(v, localityLbPolicyProp)) { + obj["localityLbPolicy"] = localityLbPolicyProp + } nameProp, err := expandComputeBackendServiceName(d.Get("name"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + outlierDetectionProp, err := expandComputeBackendServiceOutlierDetection(d.Get("outlier_detection"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("outlier_detection"); !isEmptyValue(reflect.ValueOf(outlierDetectionProp)) && (ok || !reflect.DeepEqual(v, outlierDetectionProp)) { + obj["outlierDetection"] = outlierDetectionProp + } portNameProp, err := expandComputeBackendServicePortName(d.Get("port_name"), d, config) if err != nil { return err @@ -641,6 +1030,12 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("timeout_sec"); !isEmptyValue(reflect.ValueOf(timeoutSecProp)) && (ok || !reflect.DeepEqual(v, timeoutSecProp)) { obj["timeoutSec"] = timeoutSecProp } + logConfigProp, err := expandComputeBackendServiceLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(logConfigProp)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp + } obj, err = resourceComputeBackendServiceEncoder(d, meta, obj) if err != nil { @@ -743,6 +1138,12 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) if err := d.Set("backend", flattenComputeBackendServiceBackend(res["backends"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } + if err := d.Set("circuit_breakers", flattenComputeBackendServiceCircuitBreakers(res["circuitBreakers"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } + if err := d.Set("consistent_hash", flattenComputeBackendServiceConsistentHash(res["consistentHash"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } if err := d.Set("cdn_policy", flattenComputeBackendServiceCdnPolicy(res["cdnPolicy"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } @@ -762,6 +1163,9 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) if err := d.Set("creation_timestamp", flattenComputeBackendServiceCreationTimestamp(res["creationTimestamp"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } + if err := d.Set("custom_request_headers", flattenComputeBackendServiceCustomRequestHeaders(res["customRequestHeaders"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } if err := d.Set("fingerprint", flattenComputeBackendServiceFingerprint(res["fingerprint"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } @@ -780,9 +1184,15 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) if err := d.Set("load_balancing_scheme", flattenComputeBackendServiceLoadBalancingScheme(res["loadBalancingScheme"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } + if err := d.Set("locality_lb_policy", flattenComputeBackendServiceLocalityLbPolicy(res["localityLbPolicy"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } if err := d.Set("name", flattenComputeBackendServiceName(res["name"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } + if err := d.Set("outlier_detection", flattenComputeBackendServiceOutlierDetection(res["outlierDetection"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } if err := d.Set("port_name", flattenComputeBackendServicePortName(res["portName"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } @@ -798,6 +1208,9 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) if err := d.Set("timeout_sec", flattenComputeBackendServiceTimeoutSec(res["timeoutSec"], d, config)); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } + if err := d.Set("log_config", flattenComputeBackendServiceLogConfig(res["logConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading BackendService: %s", err) + } if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { return fmt.Errorf("Error reading BackendService: %s", err) } @@ -826,6 +1239,18 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("backend"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, backendsProp)) { obj["backends"] = backendsProp } + circuitBreakersProp, err := expandComputeBackendServiceCircuitBreakers(d.Get("circuit_breakers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("circuit_breakers"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, circuitBreakersProp)) { + obj["circuitBreakers"] = circuitBreakersProp + } + consistentHashProp, err := expandComputeBackendServiceConsistentHash(d.Get("consistent_hash"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consistent_hash"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, consistentHashProp)) { + obj["consistentHash"] = consistentHashProp + } cdnPolicyProp, err := expandComputeBackendServiceCdnPolicy(d.Get("cdn_policy"), d, config) if err != nil { return err @@ -838,6 +1263,12 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{ } else if !isEmptyValue(reflect.ValueOf(connectionDrainingProp)) { obj["connectionDraining"] = connectionDrainingProp } + customRequestHeadersProp, err := expandComputeBackendServiceCustomRequestHeaders(d.Get("custom_request_headers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("custom_request_headers"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, customRequestHeadersProp)) { + obj["customRequestHeaders"] = customRequestHeadersProp + } fingerprintProp, err := expandComputeBackendServiceFingerprint(d.Get("fingerprint"), d, config) if err != nil { return err @@ -874,12 +1305,24 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("load_balancing_scheme"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, loadBalancingSchemeProp)) { obj["loadBalancingScheme"] = loadBalancingSchemeProp } + localityLbPolicyProp, err := expandComputeBackendServiceLocalityLbPolicy(d.Get("locality_lb_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("locality_lb_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, localityLbPolicyProp)) { + obj["localityLbPolicy"] = localityLbPolicyProp + } nameProp, err := expandComputeBackendServiceName(d.Get("name"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + outlierDetectionProp, err := expandComputeBackendServiceOutlierDetection(d.Get("outlier_detection"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("outlier_detection"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, outlierDetectionProp)) { + obj["outlierDetection"] = outlierDetectionProp + } portNameProp, err := expandComputeBackendServicePortName(d.Get("port_name"), d, config) if err != nil { return err @@ -910,6 +1353,12 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, timeoutSecProp)) { obj["timeoutSec"] = timeoutSecProp } + logConfigProp, err := expandComputeBackendServiceLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp + } obj, err = resourceComputeBackendServiceEncoder(d, meta, obj) if err != nil { @@ -1155,7 +1604,7 @@ func flattenComputeBackendServiceBackendMaxUtilization(v interface{}, d *schema. return v } -func flattenComputeBackendServiceCdnPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeBackendServiceCircuitBreakers(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil } @@ -1164,60 +1613,53 @@ func flattenComputeBackendServiceCdnPolicy(v interface{}, d *schema.ResourceData return nil } transformed := make(map[string]interface{}) - transformed["cache_key_policy"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicy(original["cacheKeyPolicy"], d, config) - transformed["signed_url_cache_max_age_sec"] = - flattenComputeBackendServiceCdnPolicySignedUrlCacheMaxAgeSec(original["signedUrlCacheMaxAgeSec"], d, config) + transformed["max_requests_per_connection"] = + flattenComputeBackendServiceCircuitBreakersMaxRequestsPerConnection(original["maxRequestsPerConnection"], d, config) + transformed["max_connections"] = + flattenComputeBackendServiceCircuitBreakersMaxConnections(original["maxConnections"], d, config) + transformed["max_pending_requests"] = + flattenComputeBackendServiceCircuitBreakersMaxPendingRequests(original["maxPendingRequests"], d, config) + transformed["max_requests"] = + flattenComputeBackendServiceCircuitBreakersMaxRequests(original["maxRequests"], d, config) + transformed["max_retries"] = + flattenComputeBackendServiceCircuitBreakersMaxRetries(original["maxRetries"], d, config) return []interface{}{transformed} } -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return nil - } - original := v.(map[string]interface{}) - if len(original) == 0 { - return nil +func flattenComputeBackendServiceCircuitBreakersMaxRequestsPerConnection(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } } - transformed := make(map[string]interface{}) - transformed["include_host"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeHost(original["includeHost"], d, config) - transformed["include_protocol"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeProtocol(original["includeProtocol"], d, config) - transformed["include_query_string"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeQueryString(original["includeQueryString"], d, config) - transformed["query_string_blacklist"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringBlacklist(original["queryStringBlacklist"], d, config) - transformed["query_string_whitelist"] = - flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringWhitelist(original["queryStringWhitelist"], d, config) - return []interface{}{transformed} -} -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeHost(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeQueryString(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringBlacklist(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return v +func flattenComputeBackendServiceCircuitBreakersMaxConnections(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } } - return schema.NewSet(schema.HashString, v.([]interface{})) -} -func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringWhitelist(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return v + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal } - return schema.NewSet(schema.HashString, v.([]interface{})) + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceCdnPolicySignedUrlCacheMaxAgeSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeBackendServiceCircuitBreakersMaxPendingRequests(v interface{}, d *schema.ResourceData, config *Config) interface{} { // Handles the string fixed64 format if strVal, ok := v.(string); ok { if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { @@ -1234,20 +1676,7 @@ func flattenComputeBackendServiceCdnPolicySignedUrlCacheMaxAgeSec(v interface{}, return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceConnectionDraining(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return nil - } - original := v.(map[string]interface{}) - if len(original) == 0 { - return nil - } - transformed := make(map[string]interface{}) - transformed["connection_draining_timeout_sec"] = - flattenComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(original["drainingTimeoutSec"], d, config) - return []interface{}{transformed} -} -func flattenComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeBackendServiceCircuitBreakersMaxRequests(v interface{}, d *schema.ResourceData, config *Config) interface{} { // Handles the string fixed64 format if strVal, ok := v.(string); ok { if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { @@ -1264,30 +1693,483 @@ func flattenComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec( return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} - +func flattenComputeBackendServiceCircuitBreakersMaxRetries(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceConsistentHash(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["http_cookie"] = + flattenComputeBackendServiceConsistentHashHttpCookie(original["httpCookie"], d, config) + transformed["http_header_name"] = + flattenComputeBackendServiceConsistentHashHttpHeaderName(original["httpHeaderName"], d, config) + transformed["minimum_ring_size"] = + flattenComputeBackendServiceConsistentHashMinimumRingSize(original["minimumRingSize"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceConsistentHashHttpCookie(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["ttl"] = + flattenComputeBackendServiceConsistentHashHttpCookieTtl(original["ttl"], d, config) + transformed["name"] = + flattenComputeBackendServiceConsistentHashHttpCookieName(original["name"], d, config) + transformed["path"] = + flattenComputeBackendServiceConsistentHashHttpCookiePath(original["path"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceConsistentHashHttpCookieTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["seconds"] = + flattenComputeBackendServiceConsistentHashHttpCookieTtlSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeBackendServiceConsistentHashHttpCookieTtlNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceConsistentHashHttpCookieTtlSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceConsistentHashHttpCookieTtlNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceConsistentHashHttpCookieName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceConsistentHashHttpCookiePath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceConsistentHashHttpHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceConsistentHashMinimumRingSize(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceCdnPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["cache_key_policy"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicy(original["cacheKeyPolicy"], d, config) + transformed["signed_url_cache_max_age_sec"] = + flattenComputeBackendServiceCdnPolicySignedUrlCacheMaxAgeSec(original["signedUrlCacheMaxAgeSec"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["include_host"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeHost(original["includeHost"], d, config) + transformed["include_protocol"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeProtocol(original["includeProtocol"], d, config) + transformed["include_query_string"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeQueryString(original["includeQueryString"], d, config) + transformed["query_string_blacklist"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringBlacklist(original["queryStringBlacklist"], d, config) + transformed["query_string_whitelist"] = + flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringWhitelist(original["queryStringWhitelist"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeHost(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyIncludeQueryString(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringBlacklist(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeBackendServiceCdnPolicyCacheKeyPolicyQueryStringWhitelist(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeBackendServiceCdnPolicySignedUrlCacheMaxAgeSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceConnectionDraining(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["connection_draining_timeout_sec"] = + flattenComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(original["drainingTimeoutSec"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceCustomRequestHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + func flattenComputeBackendServiceFingerprint(v interface{}, d *schema.ResourceData, config *Config) interface{} { return v } -func flattenComputeBackendServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceEnableCDN(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceHealthChecks(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) +} + +func flattenComputeBackendServiceIap(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["oauth2_client_id"] = + flattenComputeBackendServiceIapOauth2ClientId(original["oauth2ClientId"], d, config) + transformed["oauth2_client_secret"] = + flattenComputeBackendServiceIapOauth2ClientSecret(original["oauth2ClientSecret"], d, config) + transformed["oauth2_client_secret_sha256"] = + flattenComputeBackendServiceIapOauth2ClientSecretSha256(original["oauth2ClientSecretSha256"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceIapOauth2ClientId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceIapOauth2ClientSecret(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return d.Get("iap.0.oauth2_client_secret") +} + +func flattenComputeBackendServiceIapOauth2ClientSecretSha256(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceLoadBalancingScheme(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceLocalityLbPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceOutlierDetection(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["base_ejection_time"] = + flattenComputeBackendServiceOutlierDetectionBaseEjectionTime(original["baseEjectionTime"], d, config) + transformed["consecutive_errors"] = + flattenComputeBackendServiceOutlierDetectionConsecutiveErrors(original["consecutiveErrors"], d, config) + transformed["consecutive_gateway_failure"] = + flattenComputeBackendServiceOutlierDetectionConsecutiveGatewayFailure(original["consecutiveGatewayFailure"], d, config) + transformed["enforcing_consecutive_errors"] = + flattenComputeBackendServiceOutlierDetectionEnforcingConsecutiveErrors(original["enforcingConsecutiveErrors"], d, config) + transformed["enforcing_consecutive_gateway_failure"] = + flattenComputeBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(original["enforcingConsecutiveGatewayFailure"], d, config) + transformed["enforcing_success_rate"] = + flattenComputeBackendServiceOutlierDetectionEnforcingSuccessRate(original["enforcingSuccessRate"], d, config) + transformed["interval"] = + flattenComputeBackendServiceOutlierDetectionInterval(original["interval"], d, config) + transformed["max_ejection_percent"] = + flattenComputeBackendServiceOutlierDetectionMaxEjectionPercent(original["maxEjectionPercent"], d, config) + transformed["success_rate_minimum_hosts"] = + flattenComputeBackendServiceOutlierDetectionSuccessRateMinimumHosts(original["successRateMinimumHosts"], d, config) + transformed["success_rate_request_volume"] = + flattenComputeBackendServiceOutlierDetectionSuccessRateRequestVolume(original["successRateRequestVolume"], d, config) + transformed["success_rate_stdev_factor"] = + flattenComputeBackendServiceOutlierDetectionSuccessRateStdevFactor(original["successRateStdevFactor"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceOutlierDetectionBaseEjectionTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["seconds"] = + flattenComputeBackendServiceOutlierDetectionBaseEjectionTimeSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeBackendServiceOutlierDetectionBaseEjectionTimeNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceOutlierDetectionBaseEjectionTimeSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceOutlierDetectionBaseEjectionTimeNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceOutlierDetectionConsecutiveErrors(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceOutlierDetectionConsecutiveGatewayFailure(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceOutlierDetectionEnforcingConsecutiveErrors(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceEnableCDN(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceHealthChecks(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return v +func flattenComputeBackendServiceOutlierDetectionEnforcingSuccessRate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } } - return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceIap(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeBackendServiceOutlierDetectionInterval(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil } @@ -1296,32 +2178,112 @@ func flattenComputeBackendServiceIap(v interface{}, d *schema.ResourceData, conf return nil } transformed := make(map[string]interface{}) - transformed["oauth2_client_id"] = - flattenComputeBackendServiceIapOauth2ClientId(original["oauth2ClientId"], d, config) - transformed["oauth2_client_secret"] = - flattenComputeBackendServiceIapOauth2ClientSecret(original["oauth2ClientSecret"], d, config) - transformed["oauth2_client_secret_sha256"] = - flattenComputeBackendServiceIapOauth2ClientSecretSha256(original["oauth2ClientSecretSha256"], d, config) + transformed["seconds"] = + flattenComputeBackendServiceOutlierDetectionIntervalSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeBackendServiceOutlierDetectionIntervalNanos(original["nanos"], d, config) return []interface{}{transformed} } -func flattenComputeBackendServiceIapOauth2ClientId(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceOutlierDetectionIntervalSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceIapOauth2ClientSecret(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return d.Get("iap.0.oauth2_client_secret") +func flattenComputeBackendServiceOutlierDetectionIntervalNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceIapOauth2ClientSecretSha256(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceOutlierDetectionMaxEjectionPercent(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceLoadBalancingScheme(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceOutlierDetectionSuccessRateMinimumHosts(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeBackendServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v +func flattenComputeBackendServiceOutlierDetectionSuccessRateRequestVolume(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeBackendServiceOutlierDetectionSuccessRateStdevFactor(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } func flattenComputeBackendServicePortName(v interface{}, d *schema.ResourceData, config *Config) interface{} { @@ -1357,6 +2319,29 @@ func flattenComputeBackendServiceTimeoutSec(v interface{}, d *schema.ResourceDat return v // let terraform core handle it otherwise } +func flattenComputeBackendServiceLogConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enable"] = + flattenComputeBackendServiceLogConfigEnable(original["enable"], d, config) + transformed["sample_rate"] = + flattenComputeBackendServiceLogConfigSampleRate(original["sampleRate"], d, config) + return []interface{}{transformed} +} +func flattenComputeBackendServiceLogConfigEnable(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeBackendServiceLogConfigSampleRate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandComputeBackendServiceAffinityCookieTtlSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1442,59 +2427,242 @@ func expandComputeBackendServiceBackend(v interface{}, d TerraformResourceData, transformed["maxRatePerEndpoint"] = transformedMaxRatePerEndpoint } - transformedMaxUtilization, err := expandComputeBackendServiceBackendMaxUtilization(original["max_utilization"], d, config) - if err != nil { - return nil, err - } else { - transformed["maxUtilization"] = transformedMaxUtilization - } + transformedMaxUtilization, err := expandComputeBackendServiceBackendMaxUtilization(original["max_utilization"], d, config) + if err != nil { + return nil, err + } else { + transformed["maxUtilization"] = transformedMaxUtilization + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeBackendServiceBackendBalancingMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendCapacityScaler(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendGroup(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxConnectionsPerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxConnectionsPerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxRatePerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxRatePerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceBackendMaxUtilization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceCircuitBreakers(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedMaxRequestsPerConnection, err := expandComputeBackendServiceCircuitBreakersMaxRequestsPerConnection(original["max_requests_per_connection"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRequestsPerConnection); val.IsValid() && !isEmptyValue(val) { + transformed["maxRequestsPerConnection"] = transformedMaxRequestsPerConnection + } + + transformedMaxConnections, err := expandComputeBackendServiceCircuitBreakersMaxConnections(original["max_connections"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxConnections); val.IsValid() && !isEmptyValue(val) { + transformed["maxConnections"] = transformedMaxConnections + } + + transformedMaxPendingRequests, err := expandComputeBackendServiceCircuitBreakersMaxPendingRequests(original["max_pending_requests"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxPendingRequests); val.IsValid() && !isEmptyValue(val) { + transformed["maxPendingRequests"] = transformedMaxPendingRequests + } + + transformedMaxRequests, err := expandComputeBackendServiceCircuitBreakersMaxRequests(original["max_requests"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRequests); val.IsValid() && !isEmptyValue(val) { + transformed["maxRequests"] = transformedMaxRequests + } - req = append(req, transformed) + transformedMaxRetries, err := expandComputeBackendServiceCircuitBreakersMaxRetries(original["max_retries"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRetries); val.IsValid() && !isEmptyValue(val) { + transformed["maxRetries"] = transformedMaxRetries } - return req, nil + + return transformed, nil } -func expandComputeBackendServiceBackendBalancingMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceCircuitBreakersMaxRequestsPerConnection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendCapacityScaler(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceCircuitBreakersMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceCircuitBreakersMaxPendingRequests(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendGroup(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceCircuitBreakersMaxRequests(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceCircuitBreakersMaxRetries(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxConnectionsPerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHash(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHttpCookie, err := expandComputeBackendServiceConsistentHashHttpCookie(original["http_cookie"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpCookie); val.IsValid() && !isEmptyValue(val) { + transformed["httpCookie"] = transformedHttpCookie + } + + transformedHttpHeaderName, err := expandComputeBackendServiceConsistentHashHttpHeaderName(original["http_header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["httpHeaderName"] = transformedHttpHeaderName + } + + transformedMinimumRingSize, err := expandComputeBackendServiceConsistentHashMinimumRingSize(original["minimum_ring_size"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinimumRingSize); val.IsValid() && !isEmptyValue(val) { + transformed["minimumRingSize"] = transformedMinimumRingSize + } + + return transformed, nil +} + +func expandComputeBackendServiceConsistentHashHttpCookie(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedTtl, err := expandComputeBackendServiceConsistentHashHttpCookieTtl(original["ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTtl); val.IsValid() && !isEmptyValue(val) { + transformed["ttl"] = transformedTtl + } + + transformedName, err := expandComputeBackendServiceConsistentHashHttpCookieName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPath, err := expandComputeBackendServiceConsistentHashHttpCookiePath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + return transformed, nil +} + +func expandComputeBackendServiceConsistentHashHttpCookieTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeBackendServiceConsistentHashHttpCookieTtlSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeBackendServiceConsistentHashHttpCookieTtlNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeBackendServiceConsistentHashHttpCookieTtlSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxConnectionsPerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHashHttpCookieTtlNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHashHttpCookieName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxRatePerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHashHttpCookiePath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxRatePerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHashHttpHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeBackendServiceBackendMaxUtilization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeBackendServiceConsistentHashMinimumRingSize(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1613,6 +2781,11 @@ func expandComputeBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(v return v, nil } +func expandComputeBackendServiceCustomRequestHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + func expandComputeBackendServiceFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1679,10 +2852,207 @@ func expandComputeBackendServiceLoadBalancingScheme(v interface{}, d TerraformRe return v, nil } +func expandComputeBackendServiceLocalityLbPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeBackendServiceName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } +func expandComputeBackendServiceOutlierDetection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBaseEjectionTime, err := expandComputeBackendServiceOutlierDetectionBaseEjectionTime(original["base_ejection_time"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBaseEjectionTime); val.IsValid() && !isEmptyValue(val) { + transformed["baseEjectionTime"] = transformedBaseEjectionTime + } + + transformedConsecutiveErrors, err := expandComputeBackendServiceOutlierDetectionConsecutiveErrors(original["consecutive_errors"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConsecutiveErrors); val.IsValid() && !isEmptyValue(val) { + transformed["consecutiveErrors"] = transformedConsecutiveErrors + } + + transformedConsecutiveGatewayFailure, err := expandComputeBackendServiceOutlierDetectionConsecutiveGatewayFailure(original["consecutive_gateway_failure"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConsecutiveGatewayFailure); val.IsValid() && !isEmptyValue(val) { + transformed["consecutiveGatewayFailure"] = transformedConsecutiveGatewayFailure + } + + transformedEnforcingConsecutiveErrors, err := expandComputeBackendServiceOutlierDetectionEnforcingConsecutiveErrors(original["enforcing_consecutive_errors"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingConsecutiveErrors); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingConsecutiveErrors"] = transformedEnforcingConsecutiveErrors + } + + transformedEnforcingConsecutiveGatewayFailure, err := expandComputeBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(original["enforcing_consecutive_gateway_failure"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingConsecutiveGatewayFailure); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingConsecutiveGatewayFailure"] = transformedEnforcingConsecutiveGatewayFailure + } + + transformedEnforcingSuccessRate, err := expandComputeBackendServiceOutlierDetectionEnforcingSuccessRate(original["enforcing_success_rate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingSuccessRate); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingSuccessRate"] = transformedEnforcingSuccessRate + } + + transformedInterval, err := expandComputeBackendServiceOutlierDetectionInterval(original["interval"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInterval); val.IsValid() && !isEmptyValue(val) { + transformed["interval"] = transformedInterval + } + + transformedMaxEjectionPercent, err := expandComputeBackendServiceOutlierDetectionMaxEjectionPercent(original["max_ejection_percent"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxEjectionPercent); val.IsValid() && !isEmptyValue(val) { + transformed["maxEjectionPercent"] = transformedMaxEjectionPercent + } + + transformedSuccessRateMinimumHosts, err := expandComputeBackendServiceOutlierDetectionSuccessRateMinimumHosts(original["success_rate_minimum_hosts"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateMinimumHosts); val.IsValid() && !isEmptyValue(val) { + transformed["successRateMinimumHosts"] = transformedSuccessRateMinimumHosts + } + + transformedSuccessRateRequestVolume, err := expandComputeBackendServiceOutlierDetectionSuccessRateRequestVolume(original["success_rate_request_volume"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateRequestVolume); val.IsValid() && !isEmptyValue(val) { + transformed["successRateRequestVolume"] = transformedSuccessRateRequestVolume + } + + transformedSuccessRateStdevFactor, err := expandComputeBackendServiceOutlierDetectionSuccessRateStdevFactor(original["success_rate_stdev_factor"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateStdevFactor); val.IsValid() && !isEmptyValue(val) { + transformed["successRateStdevFactor"] = transformedSuccessRateStdevFactor + } + + return transformed, nil +} + +func expandComputeBackendServiceOutlierDetectionBaseEjectionTime(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeBackendServiceOutlierDetectionBaseEjectionTimeSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeBackendServiceOutlierDetectionBaseEjectionTimeNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeBackendServiceOutlierDetectionBaseEjectionTimeSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionBaseEjectionTimeNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionConsecutiveErrors(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionConsecutiveGatewayFailure(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionEnforcingConsecutiveErrors(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionEnforcingSuccessRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionInterval(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeBackendServiceOutlierDetectionIntervalSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeBackendServiceOutlierDetectionIntervalNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeBackendServiceOutlierDetectionIntervalSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionIntervalNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionMaxEjectionPercent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionSuccessRateMinimumHosts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionSuccessRateRequestVolume(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceOutlierDetectionSuccessRateStdevFactor(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeBackendServicePortName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1703,6 +3073,40 @@ func expandComputeBackendServiceTimeoutSec(v interface{}, d TerraformResourceDat return v, nil } +func expandComputeBackendServiceLogConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnable, err := expandComputeBackendServiceLogConfigEnable(original["enable"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnable); val.IsValid() && !isEmptyValue(val) { + transformed["enable"] = transformedEnable + } + + transformedSampleRate, err := expandComputeBackendServiceLogConfigSampleRate(original["sample_rate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSampleRate); val.IsValid() && !isEmptyValue(val) { + transformed["sampleRate"] = transformedSampleRate + } + + return transformed, nil +} + +func expandComputeBackendServiceLogConfigEnable(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeBackendServiceLogConfigSampleRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func resourceComputeBackendServiceEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { // The BackendService API's Update / PUT API is badly formed and behaves like // a PATCH field for at least IAP. When sent a `null` `iap` field, the API diff --git a/google/resource_compute_backend_service_test.go b/google/resource_compute_backend_service_test.go index 2f862400fff..2054a538c11 100644 --- a/google/resource_compute_backend_service_test.go +++ b/google/resource_compute_backend_service_test.go @@ -497,6 +497,182 @@ func TestAccComputeBackendService_withMaxConnectionsPerEndpoint(t *testing.T) { }) } +func TestAccComputeBackendService_withCustomHeaders(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + checkName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeBackendService_withCustomHeaders(serviceName, checkName), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeBackendService_basic(serviceName, checkName), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_internalLoadBalancing(t *testing.T) { + t.Parallel() + + fr := fmt.Sprintf("forwardrule-test-%s", randString(t, 10)) + proxy := fmt.Sprintf("forwardrule-test-%s", randString(t, 10)) + backend := fmt.Sprintf("forwardrule-test-%s", randString(t, 10)) + hc := fmt.Sprintf("forwardrule-test-%s", randString(t, 10)) + urlmap := fmt.Sprintf("forwardrule-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeBackendService_internalLoadBalancing(fr, proxy, backend, hc, urlmap), + }, + { + ResourceName: "google_compute_backend_service.backend_service", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_withLogConfig(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + checkName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeBackendService_withLogConfig(serviceName, checkName, 0.7), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeBackendService_withLogConfig(serviceName, checkName, 0.4), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_trafficDirectorUpdateBasic(t *testing.T) { + t.Parallel() + + backendName := fmt.Sprintf("foo-%s", randString(t, 10)) + checkName := fmt.Sprintf("bar-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeBackendService_trafficDirectorBasic(backendName, checkName), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeBackendService_trafficDirectorUpdateBasic(backendName, checkName), + }, + { + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeBackendService_trafficDirectorBasic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_health_check.health_check.self_link] + load_balancing_scheme = "INTERNAL_SELF_MANAGED" + locality_lb_policy = "RING_HASH" + circuit_breakers { + max_connections = 10 + } + consistent_hash { + http_cookie { + ttl { + seconds = 11 + nanos = 1234 + } + name = "mycookie" + } + } + outlier_detection { + consecutive_errors = 2 + } +} + +resource "google_compute_health_check" "health_check" { + name = "%s" + http_health_check { + port = 80 + } +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_trafficDirectorUpdateBasic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_health_check.health_check.self_link] + load_balancing_scheme = "INTERNAL_SELF_MANAGED" + locality_lb_policy = "RANDOM" + circuit_breakers { + max_connections = 10 + } + outlier_detection { + consecutive_errors = 2 + } +} + +resource "google_compute_health_check" "health_check" { + name = "%s" + http_health_check { + port = 80 + } +} +`, serviceName, checkName) +} + func testAccComputeBackendService_basic(serviceName, checkName string) string { return fmt.Sprintf(` resource "google_compute_backend_service" "foobar" { @@ -1063,3 +1239,139 @@ resource "google_compute_health_check" "default" { } `, service, maxRate, instance, neg, network, network, check) } + +func testAccComputeBackendService_withCustomHeaders(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_http_health_check.zero.self_link] + + custom_request_headers = ["Client-Region: {client_region}", "Client-Rtt: {client_rtt_msec}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_internalLoadBalancing(fr, proxy, backend, hc, urlmap string) string { + return fmt.Sprintf(` +resource "google_compute_global_forwarding_rule" "forwarding_rule" { + name = "%s" + target = google_compute_target_http_proxy.default.self_link + port_range = "80" + load_balancing_scheme = "INTERNAL_SELF_MANAGED" + ip_address = "0.0.0.0" +} + +resource "google_compute_target_http_proxy" "default" { + name = "%s" + description = "a description" + url_map = google_compute_url_map.default.self_link +} + +resource "google_compute_backend_service" "backend_service" { + name = "%s" + port_name = "http" + protocol = "HTTP" + timeout_sec = 10 + load_balancing_scheme = "INTERNAL_SELF_MANAGED" + + backend { + group = google_compute_instance_group_manager.foobar.instance_group + balancing_mode = "RATE" + capacity_scaler = 0.4 + max_rate_per_instance = 50 + } + + health_checks = [google_compute_health_check.default.self_link] +} + +resource "google_compute_health_check" "default" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + port = "80" + } +} + +resource "google_compute_url_map" "default" { + name = "%s" + description = "a description" + default_service = google_compute_backend_service.backend_service.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_backend_service.backend_service.self_link + + path_rule { + paths = ["/*"] + service = google_compute_backend_service.backend_service.self_link + } + } +} + +data "google_compute_image" "debian_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "igm-internal" + version { + instance_template = google_compute_instance_template.foobar.self_link + name = "primary" + } + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-template-internal" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = data.google_compute_image.debian_image.self_link + auto_delete = true + boot = true + } +} +`, fr, proxy, backend, hc, urlmap) +} + +func testAccComputeBackendService_withLogConfig(serviceName, checkName string, sampleRate float64) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_http_health_check.zero.self_link] + + log_config { + enable = true + sample_rate = %v + } +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, sampleRate, checkName) +} diff --git a/google/resource_compute_region_backend_service.go b/google/resource_compute_region_backend_service.go index fa4b03f2b94..4fac7b339bd 100644 --- a/google/resource_compute_region_backend_service.go +++ b/google/resource_compute_region_backend_service.go @@ -159,6 +159,16 @@ the regular expression '[a-z]([-a-z0-9]*[a-z0-9])?' which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.`, + }, + "affinity_cookie_ttl_sec": { + Type: schema.TypeInt, + Optional: true, + Description: `Lifetime of cookies in seconds if session_affinity is +GENERATED_COOKIE. If set to 0, the cookie is non-persistent and lasts +only until the end of the browser session (or equivalent). The +maximum allowed value for TTL is one day. + +When the load balancing scheme is INTERNAL, this field is not used.`, }, "backend": { Type: schema.TypeSet, @@ -167,6 +177,59 @@ character, which cannot be a dash.`, Elem: computeRegionBackendServiceBackendSchema(), Set: resourceGoogleComputeBackendServiceBackendHash, }, + "circuit_breakers": { + Type: schema.TypeList, + Optional: true, + Description: `Settings controlling the volume of connections to a backend service. This field +is applicable only when the 'load_balancing_scheme' is set to INTERNAL_MANAGED +and the 'protocol' is set to HTTP, HTTPS, or HTTP2.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_connections": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of connections to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_pending_requests": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of pending requests to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_requests": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of parallel requests to the backend cluster. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_requests_per_connection": { + Type: schema.TypeInt, + Optional: true, + Description: `Maximum requests for a single backend connection. This parameter +is respected by both the HTTP/1.1 and HTTP/2 implementations. If +not specified, there is no limit. Setting this parameter to 1 +will effectively disable keep alive.`, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + "max_retries": { + Type: schema.TypeInt, + Optional: true, + Description: `The maximum number of parallel retries to the backend cluster. +Defaults to 3.`, + Default: 3, + AtLeastOneOf: []string{"circuit_breakers.0.max_requests_per_connection", "circuit_breakers.0.max_connections", "circuit_breakers.0.max_pending_requests", "circuit_breakers.0.max_requests", "circuit_breakers.0.max_retries"}, + }, + }, + }, + }, "connection_draining_timeout_sec": { Type: schema.TypeInt, Optional: true, @@ -175,11 +238,145 @@ connections, but still work to finish started).`, Default: 0, }, + "consistent_hash": { + Type: schema.TypeList, + Optional: true, + Description: `Consistent Hash-based load balancing can be used to provide soft session +affinity based on HTTP headers, cookies or other properties. This load balancing +policy is applicable only for HTTP connections. The affinity to a particular +destination host will be lost when one or more hosts are added/removed from the +destination service. This field specifies parameters that control consistent +hashing. +This field only applies when all of the following are true - + * 'load_balancing_scheme' is set to INTERNAL_MANAGED + * 'protocol' is set to HTTP, HTTPS, or HTTP2 + * 'locality_lb_policy' is set to MAGLEV or RING_HASH`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_cookie": { + Type: schema.TypeList, + Optional: true, + Description: `Hash is based on HTTP Cookie. This field describes a HTTP cookie +that will be used as the hash key for the consistent hash load +balancer. If the cookie is not present, it will be generated. +This field is applicable if the sessionAffinity is set to HTTP_COOKIE.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Description: `Name of the cookie.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Description: `Path to set for the cookie.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + "ttl": { + Type: schema.TypeList, + Optional: true, + Description: `Lifetime of the cookie.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. +Must be from 0 to 315,576,000,000 inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond +resolution. Durations less than one second are represented +with a 0 seconds field and a positive nanos field. Must +be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie.0.ttl", "consistent_hash.0.http_cookie.0.name", "consistent_hash.0.http_cookie.0.path"}, + }, + }, + }, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + "http_header_name": { + Type: schema.TypeString, + Optional: true, + Description: `The hash based on the value of the specified header field. +This field is applicable if the sessionAffinity is set to HEADER_FIELD.`, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + "minimum_ring_size": { + Type: schema.TypeInt, + Optional: true, + Description: `The minimum number of virtual nodes to use for the hash ring. +Larger ring sizes result in more granular load +distributions. If the number of hosts in the load balancing pool +is larger than the ring size, each host will be assigned a single +virtual node. +Defaults to 1024.`, + Default: 1024, + AtLeastOneOf: []string{"consistent_hash.0.http_cookie", "consistent_hash.0.http_header_name", "consistent_hash.0.minimum_ring_size"}, + }, + }, + }, + }, "description": { Type: schema.TypeString, Optional: true, Description: `An optional description of this resource.`, }, + "failover_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Policy for failovers.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disable_connection_drain_on_failover": { + Type: schema.TypeBool, + Optional: true, + Description: `On failover or failback, this field indicates whether connection drain +will be honored. Setting this to true has the following effect: connections +to the old active pool are not drained. Connections to the new active pool +use the timeout of 10 min (currently fixed). Setting to false has the +following effect: both old and new connections will have a drain timeout +of 10 min. +This can be set to true only if the protocol is TCP. +The default is false.`, + AtLeastOneOf: []string{"failover_policy.0.disable_connection_drain_on_failover", "failover_policy.0.drop_traffic_if_unhealthy", "failover_policy.0.failover_ratio"}, + }, + "drop_traffic_if_unhealthy": { + Type: schema.TypeBool, + Optional: true, + Description: `This option is used only when no healthy VMs are detected in the primary +and backup instance groups. When set to true, traffic is dropped. When +set to false, new connections are sent across all VMs in the primary group. +The default is false.`, + AtLeastOneOf: []string{"failover_policy.0.disable_connection_drain_on_failover", "failover_policy.0.drop_traffic_if_unhealthy", "failover_policy.0.failover_ratio"}, + }, + "failover_ratio": { + Type: schema.TypeFloat, + Optional: true, + Description: `The value of the field must be in [0, 1]. If the ratio of the healthy +VMs in the primary backend is at or below this number, traffic arriving +at the load-balanced IP will be directed to the failover backend. +In case where 'failoverRatio' is not set or all the VMs in the backup +backend are unhealthy, the traffic will be directed back to the primary +backend in the "force" mode, where traffic will be spread to the healthy +VMs with the best effort, or to all VMs when no VM is healthy. +This field is only used with l4 load balancing.`, + AtLeastOneOf: []string{"failover_policy.0.disable_connection_drain_on_failover", "failover_policy.0.drop_traffic_if_unhealthy", "failover_policy.0.failover_ratio"}, + }, + }, + }, + }, "load_balancing_scheme": { Type: schema.TypeString, Optional: true, @@ -190,6 +387,66 @@ will be used for. A backend service created for one type of load balancing cannot be used with the other(s). Default value: "INTERNAL" Possible values: ["INTERNAL", "INTERNAL_MANAGED"]`, Default: "INTERNAL", }, + "locality_lb_policy": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV", ""}, false), + Description: `The load balancing algorithm used within the scope of the locality. +The possible values are - + +ROUND_ROBIN - This is a simple policy in which each healthy backend + is selected in round robin order. + +LEAST_REQUEST - An O(1) algorithm which selects two random healthy + hosts and picks the host which has fewer active requests. + +RING_HASH - The ring/modulo hash load balancer implements consistent + hashing to backends. The algorithm has the property that the + addition/removal of a host from a set of N hosts only affects + 1/N of the requests. + +RANDOM - The load balancer selects a random healthy host. + +ORIGINAL_DESTINATION - Backend host is selected based on the client + connection metadata, i.e., connections are opened + to the same address as the destination address of + the incoming connection before the connection + was redirected to the load balancer. + +MAGLEV - used as a drop in replacement for the ring hash load balancer. + Maglev is not as stable as ring hash but has faster table lookup + build times and host selection times. For more information about + Maglev, refer to https://ai.google/research/pubs/pub44824 + +This field is applicable only when the 'load_balancing_scheme' is set to +INTERNAL_MANAGED and the 'protocol' is set to HTTP, HTTPS, or HTTP2. Possible values: ["ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV"]`, + }, + "log_config": { + Type: schema.TypeList, + Optional: true, + Description: `This field denotes the logging options for the load balancer traffic served by this backend service. +If logging is enabled, logs will be exported to Stackdriver.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether to enable logging for the load balancer traffic served by this backend service.`, + AtLeastOneOf: []string{"log_config.0.enable", "log_config.0.sample_rate"}, + }, + "sample_rate": { + Type: schema.TypeFloat, + Optional: true, + Description: `This field can only be specified if logging is enabled for this backend service. The value of +the field must be in [0, 1]. This configures the sampling rate of requests to the load balancer +where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. +The default value is 1.0.`, + AtLeastOneOf: []string{"log_config.0.enable", "log_config.0.sample_rate"}, + }, + }, + }, + }, "network": { Type: schema.TypeString, Optional: true, @@ -197,6 +454,155 @@ balancing cannot be used with the other(s). Default value: "INTERNAL" Possible v Description: `The URL of the network to which this backend service belongs. This field can only be specified when the load balancing scheme is set to INTERNAL.`, }, + "outlier_detection": { + Type: schema.TypeList, + Optional: true, + Description: `Settings controlling eviction of unhealthy hosts from the load balancing pool. +This field is applicable only when the 'load_balancing_scheme' is set +to INTERNAL_MANAGED and the 'protocol' is set to HTTP, HTTPS, or HTTP2.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "base_ejection_time": { + Type: schema.TypeList, + Optional: true, + Description: `The base time that a host is ejected for. The real time is equal to the base +time multiplied by the number of times the host has been ejected. Defaults to +30000ms or 30s.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "consecutive_errors": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of errors before a host is ejected from the connection pool. When the +backend host is accessed over HTTP, a 5xx return code qualifies as an error. +Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "consecutive_gateway_failure": { + Type: schema.TypeInt, + Optional: true, + Description: `The number of consecutive gateway failures (502, 503, 504 status or connection +errors that are mapped to one of those status codes) before a consecutive +gateway failure ejection occurs. Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_consecutive_errors": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through consecutive 5xx. This setting can be used to disable +ejection or to ramp it up slowly. Defaults to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_consecutive_gateway_failure": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through consecutive gateway failures. This setting can be +used to disable ejection or to ramp it up slowly. Defaults to 0.`, + Default: 0, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "enforcing_success_rate": { + Type: schema.TypeInt, + Optional: true, + Description: `The percentage chance that a host will be actually ejected when an outlier +status is detected through success rate statistics. This setting can be used to +disable ejection or to ramp it up slowly. Defaults to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "interval": { + Type: schema.TypeList, + Optional: true, + Description: `Time interval between ejection sweep analysis. This can result in both new +ejections as well as hosts being returned to service. Defaults to 10 seconds.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeInt, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "max_ejection_percent": { + Type: schema.TypeInt, + Optional: true, + Description: `Maximum percentage of hosts in the load balancing pool for the backend service +that can be ejected. Defaults to 10%.`, + Default: 10, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_minimum_hosts": { + Type: schema.TypeInt, + Optional: true, + Description: `The number of hosts in a cluster that must have enough request volume to detect +success rate outliers. If the number of hosts is less than this setting, outlier +detection via success rate statistics is not performed for any host in the +cluster. Defaults to 5.`, + Default: 5, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_request_volume": { + Type: schema.TypeInt, + Optional: true, + Description: `The minimum number of total requests that must be collected in one interval (as +defined by the interval duration above) to include this host in success rate +based outlier detection. If the volume is lower than this setting, outlier +detection via success rate statistics is not performed for that host. Defaults +to 100.`, + Default: 100, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + "success_rate_stdev_factor": { + Type: schema.TypeInt, + Optional: true, + Description: `This factor is used to determine the ejection threshold for success rate outlier +ejection. The ejection threshold is the difference between the mean success +rate, and the product of this factor and the standard deviation of the mean +success rate: mean - (stdev * success_rate_stdev_factor). This factor is divided +by a thousand to get a double. That is, if the desired factor is 1.9, the +runtime value should be 1900. Defaults to 1900.`, + Default: 1900, + AtLeastOneOf: []string{"outlier_detection.0.base_ejection_time", "outlier_detection.0.consecutive_errors", "outlier_detection.0.consecutive_gateway_failure", "outlier_detection.0.enforcing_consecutive_errors", "outlier_detection.0.enforcing_consecutive_gateway_failure", "outlier_detection.0.enforcing_success_rate", "outlier_detection.0.interval", "outlier_detection.0.max_ejection_percent", "outlier_detection.0.success_rate_minimum_hosts", "outlier_detection.0.success_rate_request_volume", "outlier_detection.0.success_rate_stdev_factor"}, + }, + }, + }, + }, "protocol": { Type: schema.TypeString, Computed: true, @@ -308,6 +714,13 @@ A setting of 0 means the group is completely drained, offering Optional: true, Description: `An optional description of this resource. Provide this property when you create the resource.`, + }, + "failover": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `This field designates whether this is a failover backend. More +than one failover backend can be configured for a given RegionBackendService.`, }, "max_connections": { Type: schema.TypeInt, @@ -388,12 +801,30 @@ func resourceComputeRegionBackendServiceCreate(d *schema.ResourceData, meta inte config := meta.(*Config) obj := make(map[string]interface{}) + affinityCookieTtlSecProp, err := expandComputeRegionBackendServiceAffinityCookieTtlSec(d.Get("affinity_cookie_ttl_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("affinity_cookie_ttl_sec"); !isEmptyValue(reflect.ValueOf(affinityCookieTtlSecProp)) && (ok || !reflect.DeepEqual(v, affinityCookieTtlSecProp)) { + obj["affinityCookieTtlSec"] = affinityCookieTtlSecProp + } backendsProp, err := expandComputeRegionBackendServiceBackend(d.Get("backend"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("backend"); !isEmptyValue(reflect.ValueOf(backendsProp)) && (ok || !reflect.DeepEqual(v, backendsProp)) { obj["backends"] = backendsProp } + circuitBreakersProp, err := expandComputeRegionBackendServiceCircuitBreakers(d.Get("circuit_breakers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("circuit_breakers"); !isEmptyValue(reflect.ValueOf(circuitBreakersProp)) && (ok || !reflect.DeepEqual(v, circuitBreakersProp)) { + obj["circuitBreakers"] = circuitBreakersProp + } + consistentHashProp, err := expandComputeRegionBackendServiceConsistentHash(d.Get("consistent_hash"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consistent_hash"); !isEmptyValue(reflect.ValueOf(consistentHashProp)) && (ok || !reflect.DeepEqual(v, consistentHashProp)) { + obj["consistentHash"] = consistentHashProp + } connectionDrainingProp, err := expandComputeRegionBackendServiceConnectionDraining(nil, d, config) if err != nil { return err @@ -406,6 +837,12 @@ func resourceComputeRegionBackendServiceCreate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { obj["description"] = descriptionProp } + failoverPolicyProp, err := expandComputeRegionBackendServiceFailoverPolicy(d.Get("failover_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("failover_policy"); !isEmptyValue(reflect.ValueOf(failoverPolicyProp)) && (ok || !reflect.DeepEqual(v, failoverPolicyProp)) { + obj["failoverPolicy"] = failoverPolicyProp + } fingerprintProp, err := expandComputeRegionBackendServiceFingerprint(d.Get("fingerprint"), d, config) if err != nil { return err @@ -424,12 +861,24 @@ func resourceComputeRegionBackendServiceCreate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("load_balancing_scheme"); !isEmptyValue(reflect.ValueOf(loadBalancingSchemeProp)) && (ok || !reflect.DeepEqual(v, loadBalancingSchemeProp)) { obj["loadBalancingScheme"] = loadBalancingSchemeProp } + localityLbPolicyProp, err := expandComputeRegionBackendServiceLocalityLbPolicy(d.Get("locality_lb_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("locality_lb_policy"); !isEmptyValue(reflect.ValueOf(localityLbPolicyProp)) && (ok || !reflect.DeepEqual(v, localityLbPolicyProp)) { + obj["localityLbPolicy"] = localityLbPolicyProp + } nameProp, err := expandComputeRegionBackendServiceName(d.Get("name"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + outlierDetectionProp, err := expandComputeRegionBackendServiceOutlierDetection(d.Get("outlier_detection"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("outlier_detection"); !isEmptyValue(reflect.ValueOf(outlierDetectionProp)) && (ok || !reflect.DeepEqual(v, outlierDetectionProp)) { + obj["outlierDetection"] = outlierDetectionProp + } protocolProp, err := expandComputeRegionBackendServiceProtocol(d.Get("protocol"), d, config) if err != nil { return err @@ -448,6 +897,12 @@ func resourceComputeRegionBackendServiceCreate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("timeout_sec"); !isEmptyValue(reflect.ValueOf(timeoutSecProp)) && (ok || !reflect.DeepEqual(v, timeoutSecProp)) { obj["timeoutSec"] = timeoutSecProp } + logConfigProp, err := expandComputeRegionBackendServiceLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(logConfigProp)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp + } networkProp, err := expandComputeRegionBackendServiceNetwork(d.Get("network"), d, config) if err != nil { return err @@ -524,9 +979,18 @@ func resourceComputeRegionBackendServiceRead(d *schema.ResourceData, meta interf return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("affinity_cookie_ttl_sec", flattenComputeRegionBackendServiceAffinityCookieTtlSec(res["affinityCookieTtlSec"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } if err := d.Set("backend", flattenComputeRegionBackendServiceBackend(res["backends"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("circuit_breakers", flattenComputeRegionBackendServiceCircuitBreakers(res["circuitBreakers"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } + if err := d.Set("consistent_hash", flattenComputeRegionBackendServiceConsistentHash(res["consistentHash"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } // Terraform must set the top level schema field, but since this object contains collapsed properties // it's difficult to know what the top level should be. Instead we just loop over the map returned from flatten. if flattenedProp := flattenComputeRegionBackendServiceConnectionDraining(res["connectionDraining"], d, config); flattenedProp != nil { @@ -546,6 +1010,9 @@ func resourceComputeRegionBackendServiceRead(d *schema.ResourceData, meta interf if err := d.Set("description", flattenComputeRegionBackendServiceDescription(res["description"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("failover_policy", flattenComputeRegionBackendServiceFailoverPolicy(res["failoverPolicy"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } if err := d.Set("fingerprint", flattenComputeRegionBackendServiceFingerprint(res["fingerprint"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } @@ -555,9 +1022,15 @@ func resourceComputeRegionBackendServiceRead(d *schema.ResourceData, meta interf if err := d.Set("load_balancing_scheme", flattenComputeRegionBackendServiceLoadBalancingScheme(res["loadBalancingScheme"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("locality_lb_policy", flattenComputeRegionBackendServiceLocalityLbPolicy(res["localityLbPolicy"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } if err := d.Set("name", flattenComputeRegionBackendServiceName(res["name"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("outlier_detection", flattenComputeRegionBackendServiceOutlierDetection(res["outlierDetection"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } if err := d.Set("protocol", flattenComputeRegionBackendServiceProtocol(res["protocol"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } @@ -567,6 +1040,9 @@ func resourceComputeRegionBackendServiceRead(d *schema.ResourceData, meta interf if err := d.Set("timeout_sec", flattenComputeRegionBackendServiceTimeoutSec(res["timeoutSec"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } + if err := d.Set("log_config", flattenComputeRegionBackendServiceLogConfig(res["logConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionBackendService: %s", err) + } if err := d.Set("network", flattenComputeRegionBackendServiceNetwork(res["network"], d, config)); err != nil { return fmt.Errorf("Error reading RegionBackendService: %s", err) } @@ -589,12 +1065,30 @@ func resourceComputeRegionBackendServiceUpdate(d *schema.ResourceData, meta inte } obj := make(map[string]interface{}) + affinityCookieTtlSecProp, err := expandComputeRegionBackendServiceAffinityCookieTtlSec(d.Get("affinity_cookie_ttl_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("affinity_cookie_ttl_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, affinityCookieTtlSecProp)) { + obj["affinityCookieTtlSec"] = affinityCookieTtlSecProp + } backendsProp, err := expandComputeRegionBackendServiceBackend(d.Get("backend"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("backend"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, backendsProp)) { obj["backends"] = backendsProp } + circuitBreakersProp, err := expandComputeRegionBackendServiceCircuitBreakers(d.Get("circuit_breakers"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("circuit_breakers"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, circuitBreakersProp)) { + obj["circuitBreakers"] = circuitBreakersProp + } + consistentHashProp, err := expandComputeRegionBackendServiceConsistentHash(d.Get("consistent_hash"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consistent_hash"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, consistentHashProp)) { + obj["consistentHash"] = consistentHashProp + } connectionDrainingProp, err := expandComputeRegionBackendServiceConnectionDraining(nil, d, config) if err != nil { return err @@ -607,6 +1101,12 @@ func resourceComputeRegionBackendServiceUpdate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { obj["description"] = descriptionProp } + failoverPolicyProp, err := expandComputeRegionBackendServiceFailoverPolicy(d.Get("failover_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("failover_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, failoverPolicyProp)) { + obj["failoverPolicy"] = failoverPolicyProp + } fingerprintProp, err := expandComputeRegionBackendServiceFingerprint(d.Get("fingerprint"), d, config) if err != nil { return err @@ -625,12 +1125,24 @@ func resourceComputeRegionBackendServiceUpdate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("load_balancing_scheme"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, loadBalancingSchemeProp)) { obj["loadBalancingScheme"] = loadBalancingSchemeProp } + localityLbPolicyProp, err := expandComputeRegionBackendServiceLocalityLbPolicy(d.Get("locality_lb_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("locality_lb_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, localityLbPolicyProp)) { + obj["localityLbPolicy"] = localityLbPolicyProp + } nameProp, err := expandComputeRegionBackendServiceName(d.Get("name"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + outlierDetectionProp, err := expandComputeRegionBackendServiceOutlierDetection(d.Get("outlier_detection"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("outlier_detection"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, outlierDetectionProp)) { + obj["outlierDetection"] = outlierDetectionProp + } protocolProp, err := expandComputeRegionBackendServiceProtocol(d.Get("protocol"), d, config) if err != nil { return err @@ -649,6 +1161,12 @@ func resourceComputeRegionBackendServiceUpdate(d *schema.ResourceData, meta inte } else if v, ok := d.GetOkExists("timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, timeoutSecProp)) { obj["timeoutSec"] = timeoutSecProp } + logConfigProp, err := expandComputeRegionBackendServiceLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp + } networkProp, err := expandComputeRegionBackendServiceNetwork(d.Get("network"), d, config) if err != nil { return err @@ -744,6 +1262,23 @@ func resourceComputeRegionBackendServiceImport(d *schema.ResourceData, meta inte return []*schema.ResourceData{d}, nil } +func flattenComputeRegionBackendServiceAffinityCookieTtlSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + func flattenComputeRegionBackendServiceBackend(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -760,6 +1295,7 @@ func flattenComputeRegionBackendServiceBackend(v interface{}, d *schema.Resource "balancing_mode": flattenComputeRegionBackendServiceBackendBalancingMode(original["balancingMode"], d, config), "capacity_scaler": flattenComputeRegionBackendServiceBackendCapacityScaler(original["capacityScaler"], d, config), "description": flattenComputeRegionBackendServiceBackendDescription(original["description"], d, config), + "failover": flattenComputeRegionBackendServiceBackendFailover(original["failover"], d, config), "group": flattenComputeRegionBackendServiceBackendGroup(original["group"], d, config), "max_connections": flattenComputeRegionBackendServiceBackendMaxConnections(original["maxConnections"], d, config), "max_connections_per_instance": flattenComputeRegionBackendServiceBackendMaxConnectionsPerInstance(original["maxConnectionsPerInstance"], d, config), @@ -784,6 +1320,10 @@ func flattenComputeRegionBackendServiceBackendDescription(v interface{}, d *sche return v } +func flattenComputeRegionBackendServiceBackendFailover(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenComputeRegionBackendServiceBackendGroup(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -871,7 +1411,7 @@ func flattenComputeRegionBackendServiceBackendMaxUtilization(v interface{}, d *s return v } -func flattenComputeRegionBackendServiceConnectionDraining(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeRegionBackendServiceCircuitBreakers(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil } @@ -880,11 +1420,19 @@ func flattenComputeRegionBackendServiceConnectionDraining(v interface{}, d *sche return nil } transformed := make(map[string]interface{}) - transformed["connection_draining_timeout_sec"] = - flattenComputeRegionBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(original["drainingTimeoutSec"], d, config) + transformed["max_requests_per_connection"] = + flattenComputeRegionBackendServiceCircuitBreakersMaxRequestsPerConnection(original["maxRequestsPerConnection"], d, config) + transformed["max_connections"] = + flattenComputeRegionBackendServiceCircuitBreakersMaxConnections(original["maxConnections"], d, config) + transformed["max_pending_requests"] = + flattenComputeRegionBackendServiceCircuitBreakersMaxPendingRequests(original["maxPendingRequests"], d, config) + transformed["max_requests"] = + flattenComputeRegionBackendServiceCircuitBreakersMaxRequests(original["maxRequests"], d, config) + transformed["max_retries"] = + flattenComputeRegionBackendServiceCircuitBreakersMaxRetries(original["maxRetries"], d, config) return []interface{}{transformed} } -func flattenComputeRegionBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeRegionBackendServiceCircuitBreakersMaxRequestsPerConnection(v interface{}, d *schema.ResourceData, config *Config) interface{} { // Handles the string fixed64 format if strVal, ok := v.(string); ok { if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { @@ -901,42 +1449,58 @@ func flattenComputeRegionBackendServiceConnectionDrainingConnectionDrainingTimeo return v // let terraform core handle it otherwise } -func flattenComputeRegionBackendServiceCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} +func flattenComputeRegionBackendServiceCircuitBreakersMaxConnections(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } -func flattenComputeRegionBackendServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } -func flattenComputeRegionBackendServiceFingerprint(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v + return v // let terraform core handle it otherwise } -func flattenComputeRegionBackendServiceHealthChecks(v interface{}, d *schema.ResourceData, config *Config) interface{} { - if v == nil { - return v +func flattenComputeRegionBackendServiceCircuitBreakersMaxPendingRequests(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } } - return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) -} -func flattenComputeRegionBackendServiceLoadBalancingScheme(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } -func flattenComputeRegionBackendServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v + return v // let terraform core handle it otherwise } -func flattenComputeRegionBackendServiceProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v -} +func flattenComputeRegionBackendServiceCircuitBreakersMaxRequests(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } -func flattenComputeRegionBackendServiceSessionAffinity(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return v + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise } -func flattenComputeRegionBackendServiceTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeRegionBackendServiceCircuitBreakersMaxRetries(v interface{}, d *schema.ResourceData, config *Config) interface{} { // Handles the string fixed64 format if strVal, ok := v.(string); ok { if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { @@ -953,58 +1517,603 @@ func flattenComputeRegionBackendServiceTimeoutSec(v interface{}, d *schema.Resou return v // let terraform core handle it otherwise } -func flattenComputeRegionBackendServiceNetwork(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeRegionBackendServiceConsistentHash(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { - return v + return nil } - return ConvertSelfLinkToV1(v.(string)) + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["http_cookie"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookie(original["httpCookie"], d, config) + transformed["http_header_name"] = + flattenComputeRegionBackendServiceConsistentHashHttpHeaderName(original["httpHeaderName"], d, config) + transformed["minimum_ring_size"] = + flattenComputeRegionBackendServiceConsistentHashMinimumRingSize(original["minimumRingSize"], d, config) + return []interface{}{transformed} } - -func flattenComputeRegionBackendServiceRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { +func flattenComputeRegionBackendServiceConsistentHashHttpCookie(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { - return v + return nil } - return NameFromSelfLinkStateFunc(v) + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["ttl"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookieTtl(original["ttl"], d, config) + transformed["name"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookieName(original["name"], d, config) + transformed["path"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookiePath(original["path"], d, config) + return []interface{}{transformed} } - -func expandComputeRegionBackendServiceBackend(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { - v = v.(*schema.Set).List() - l := v.([]interface{}) - req := make([]interface{}, 0, len(l)) - for _, raw := range l { - if raw == nil { - continue +func flattenComputeRegionBackendServiceConsistentHashHttpCookieTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["seconds"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookieTtlSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeRegionBackendServiceConsistentHashHttpCookieTtlNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceConsistentHashHttpCookieTtlSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal } - original := raw.(map[string]interface{}) - transformed := make(map[string]interface{}) + } - transformedBalancingMode, err := expandComputeRegionBackendServiceBackendBalancingMode(original["balancing_mode"], d, config) - if err != nil { - return nil, err - } else if val := reflect.ValueOf(transformedBalancingMode); val.IsValid() && !isEmptyValue(val) { - transformed["balancingMode"] = transformedBalancingMode - } + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } - transformedCapacityScaler, err := expandComputeRegionBackendServiceBackendCapacityScaler(original["capacity_scaler"], d, config) - if err != nil { - return nil, err - } else { - transformed["capacityScaler"] = transformedCapacityScaler - } + return v // let terraform core handle it otherwise +} - transformedDescription, err := expandComputeRegionBackendServiceBackendDescription(original["description"], d, config) - if err != nil { - return nil, err - } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { - transformed["description"] = transformedDescription +func flattenComputeRegionBackendServiceConsistentHashHttpCookieTtlNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal } + } - transformedGroup, err := expandComputeRegionBackendServiceBackendGroup(original["group"], d, config) - if err != nil { - return nil, err - } else if val := reflect.ValueOf(transformedGroup); val.IsValid() && !isEmptyValue(val) { - transformed["group"] = transformedGroup - } + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceConsistentHashHttpCookieName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceConsistentHashHttpCookiePath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceConsistentHashHttpHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceConsistentHashMinimumRingSize(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceConnectionDraining(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["connection_draining_timeout_sec"] = + flattenComputeRegionBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(original["drainingTimeoutSec"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceConnectionDrainingConnectionDrainingTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceFailoverPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["disable_connection_drain_on_failover"] = + flattenComputeRegionBackendServiceFailoverPolicyDisableConnectionDrainOnFailover(original["disableConnectionDrainOnFailover"], d, config) + transformed["drop_traffic_if_unhealthy"] = + flattenComputeRegionBackendServiceFailoverPolicyDropTrafficIfUnhealthy(original["dropTrafficIfUnhealthy"], d, config) + transformed["failover_ratio"] = + flattenComputeRegionBackendServiceFailoverPolicyFailoverRatio(original["failoverRatio"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceFailoverPolicyDisableConnectionDrainOnFailover(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceFailoverPolicyDropTrafficIfUnhealthy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceFailoverPolicyFailoverRatio(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceFingerprint(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceHealthChecks(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) +} + +func flattenComputeRegionBackendServiceLoadBalancingScheme(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceLocalityLbPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceOutlierDetection(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["base_ejection_time"] = + flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTime(original["baseEjectionTime"], d, config) + transformed["consecutive_errors"] = + flattenComputeRegionBackendServiceOutlierDetectionConsecutiveErrors(original["consecutiveErrors"], d, config) + transformed["consecutive_gateway_failure"] = + flattenComputeRegionBackendServiceOutlierDetectionConsecutiveGatewayFailure(original["consecutiveGatewayFailure"], d, config) + transformed["enforcing_consecutive_errors"] = + flattenComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveErrors(original["enforcingConsecutiveErrors"], d, config) + transformed["enforcing_consecutive_gateway_failure"] = + flattenComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(original["enforcingConsecutiveGatewayFailure"], d, config) + transformed["enforcing_success_rate"] = + flattenComputeRegionBackendServiceOutlierDetectionEnforcingSuccessRate(original["enforcingSuccessRate"], d, config) + transformed["interval"] = + flattenComputeRegionBackendServiceOutlierDetectionInterval(original["interval"], d, config) + transformed["max_ejection_percent"] = + flattenComputeRegionBackendServiceOutlierDetectionMaxEjectionPercent(original["maxEjectionPercent"], d, config) + transformed["success_rate_minimum_hosts"] = + flattenComputeRegionBackendServiceOutlierDetectionSuccessRateMinimumHosts(original["successRateMinimumHosts"], d, config) + transformed["success_rate_request_volume"] = + flattenComputeRegionBackendServiceOutlierDetectionSuccessRateRequestVolume(original["successRateRequestVolume"], d, config) + transformed["success_rate_stdev_factor"] = + flattenComputeRegionBackendServiceOutlierDetectionSuccessRateStdevFactor(original["successRateStdevFactor"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["seconds"] = + flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionConsecutiveErrors(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionConsecutiveGatewayFailure(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveErrors(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionEnforcingSuccessRate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionInterval(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["seconds"] = + flattenComputeRegionBackendServiceOutlierDetectionIntervalSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenComputeRegionBackendServiceOutlierDetectionIntervalNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceOutlierDetectionIntervalSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionIntervalNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionMaxEjectionPercent(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionSuccessRateMinimumHosts(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionSuccessRateRequestVolume(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceOutlierDetectionSuccessRateStdevFactor(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceSessionAffinity(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceTimeoutSec(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionBackendServiceLogConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enable"] = + flattenComputeRegionBackendServiceLogConfigEnable(original["enable"], d, config) + transformed["sample_rate"] = + flattenComputeRegionBackendServiceLogConfigSampleRate(original["sampleRate"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionBackendServiceLogConfigEnable(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceLogConfigSampleRate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionBackendServiceNetwork(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionBackendServiceRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return NameFromSelfLinkStateFunc(v) +} + +func expandComputeRegionBackendServiceAffinityCookieTtlSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackend(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBalancingMode, err := expandComputeRegionBackendServiceBackendBalancingMode(original["balancing_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBalancingMode); val.IsValid() && !isEmptyValue(val) { + transformed["balancingMode"] = transformedBalancingMode + } + + transformedCapacityScaler, err := expandComputeRegionBackendServiceBackendCapacityScaler(original["capacity_scaler"], d, config) + if err != nil { + return nil, err + } else { + transformed["capacityScaler"] = transformedCapacityScaler + } + + transformedDescription, err := expandComputeRegionBackendServiceBackendDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedFailover, err := expandComputeRegionBackendServiceBackendFailover(original["failover"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFailover); val.IsValid() && !isEmptyValue(val) { + transformed["failover"] = transformedFailover + } + + transformedGroup, err := expandComputeRegionBackendServiceBackendGroup(original["group"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGroup); val.IsValid() && !isEmptyValue(val) { + transformed["group"] = transformedGroup + } transformedMaxConnections, err := expandComputeRegionBackendServiceBackendMaxConnections(original["max_connections"], d, config) if err != nil { @@ -1048,59 +2157,246 @@ func expandComputeRegionBackendServiceBackend(v interface{}, d TerraformResource transformed["maxRatePerEndpoint"] = transformedMaxRatePerEndpoint } - transformedMaxUtilization, err := expandComputeRegionBackendServiceBackendMaxUtilization(original["max_utilization"], d, config) - if err != nil { - return nil, err - } else if val := reflect.ValueOf(transformedMaxUtilization); val.IsValid() && !isEmptyValue(val) { - transformed["maxUtilization"] = transformedMaxUtilization - } + transformedMaxUtilization, err := expandComputeRegionBackendServiceBackendMaxUtilization(original["max_utilization"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxUtilization); val.IsValid() && !isEmptyValue(val) { + transformed["maxUtilization"] = transformedMaxUtilization + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionBackendServiceBackendBalancingMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendCapacityScaler(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendFailover(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendGroup(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxConnectionsPerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxConnectionsPerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxRatePerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxRatePerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceBackendMaxUtilization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceCircuitBreakers(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedMaxRequestsPerConnection, err := expandComputeRegionBackendServiceCircuitBreakersMaxRequestsPerConnection(original["max_requests_per_connection"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRequestsPerConnection); val.IsValid() && !isEmptyValue(val) { + transformed["maxRequestsPerConnection"] = transformedMaxRequestsPerConnection + } + + transformedMaxConnections, err := expandComputeRegionBackendServiceCircuitBreakersMaxConnections(original["max_connections"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxConnections); val.IsValid() && !isEmptyValue(val) { + transformed["maxConnections"] = transformedMaxConnections + } + + transformedMaxPendingRequests, err := expandComputeRegionBackendServiceCircuitBreakersMaxPendingRequests(original["max_pending_requests"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxPendingRequests); val.IsValid() && !isEmptyValue(val) { + transformed["maxPendingRequests"] = transformedMaxPendingRequests + } - req = append(req, transformed) + transformedMaxRequests, err := expandComputeRegionBackendServiceCircuitBreakersMaxRequests(original["max_requests"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRequests); val.IsValid() && !isEmptyValue(val) { + transformed["maxRequests"] = transformedMaxRequests } - return req, nil + + transformedMaxRetries, err := expandComputeRegionBackendServiceCircuitBreakersMaxRetries(original["max_retries"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxRetries); val.IsValid() && !isEmptyValue(val) { + transformed["maxRetries"] = transformedMaxRetries + } + + return transformed, nil } -func expandComputeRegionBackendServiceBackendBalancingMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceCircuitBreakersMaxRequestsPerConnection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendCapacityScaler(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceCircuitBreakersMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceCircuitBreakersMaxPendingRequests(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendGroup(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceCircuitBreakersMaxRequests(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxConnections(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceCircuitBreakersMaxRetries(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxConnectionsPerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHash(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHttpCookie, err := expandComputeRegionBackendServiceConsistentHashHttpCookie(original["http_cookie"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpCookie); val.IsValid() && !isEmptyValue(val) { + transformed["httpCookie"] = transformedHttpCookie + } + + transformedHttpHeaderName, err := expandComputeRegionBackendServiceConsistentHashHttpHeaderName(original["http_header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["httpHeaderName"] = transformedHttpHeaderName + } + + transformedMinimumRingSize, err := expandComputeRegionBackendServiceConsistentHashMinimumRingSize(original["minimum_ring_size"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinimumRingSize); val.IsValid() && !isEmptyValue(val) { + transformed["minimumRingSize"] = transformedMinimumRingSize + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceConsistentHashHttpCookie(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedTtl, err := expandComputeRegionBackendServiceConsistentHashHttpCookieTtl(original["ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTtl); val.IsValid() && !isEmptyValue(val) { + transformed["ttl"] = transformedTtl + } + + transformedName, err := expandComputeRegionBackendServiceConsistentHashHttpCookieName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPath, err := expandComputeRegionBackendServiceConsistentHashHttpCookiePath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceConsistentHashHttpCookieTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeRegionBackendServiceConsistentHashHttpCookieTtlSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeRegionBackendServiceConsistentHashHttpCookieTtlNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceConsistentHashHttpCookieTtlSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxConnectionsPerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHashHttpCookieTtlNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHashHttpCookieName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxRatePerInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHashHttpCookiePath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxRatePerEndpoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHashHttpHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } -func expandComputeRegionBackendServiceBackendMaxUtilization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { +func expandComputeRegionBackendServiceConsistentHashMinimumRingSize(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1124,6 +2420,51 @@ func expandComputeRegionBackendServiceDescription(v interface{}, d TerraformReso return v, nil } +func expandComputeRegionBackendServiceFailoverPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDisableConnectionDrainOnFailover, err := expandComputeRegionBackendServiceFailoverPolicyDisableConnectionDrainOnFailover(original["disable_connection_drain_on_failover"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisableConnectionDrainOnFailover); val.IsValid() && !isEmptyValue(val) { + transformed["disableConnectionDrainOnFailover"] = transformedDisableConnectionDrainOnFailover + } + + transformedDropTrafficIfUnhealthy, err := expandComputeRegionBackendServiceFailoverPolicyDropTrafficIfUnhealthy(original["drop_traffic_if_unhealthy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDropTrafficIfUnhealthy); val.IsValid() && !isEmptyValue(val) { + transformed["dropTrafficIfUnhealthy"] = transformedDropTrafficIfUnhealthy + } + + transformedFailoverRatio, err := expandComputeRegionBackendServiceFailoverPolicyFailoverRatio(original["failover_ratio"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFailoverRatio); val.IsValid() && !isEmptyValue(val) { + transformed["failoverRatio"] = transformedFailoverRatio + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceFailoverPolicyDisableConnectionDrainOnFailover(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceFailoverPolicyDropTrafficIfUnhealthy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceFailoverPolicyFailoverRatio(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeRegionBackendServiceFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1137,10 +2478,207 @@ func expandComputeRegionBackendServiceLoadBalancingScheme(v interface{}, d Terra return v, nil } +func expandComputeRegionBackendServiceLocalityLbPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeRegionBackendServiceName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } +func expandComputeRegionBackendServiceOutlierDetection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBaseEjectionTime, err := expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTime(original["base_ejection_time"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBaseEjectionTime); val.IsValid() && !isEmptyValue(val) { + transformed["baseEjectionTime"] = transformedBaseEjectionTime + } + + transformedConsecutiveErrors, err := expandComputeRegionBackendServiceOutlierDetectionConsecutiveErrors(original["consecutive_errors"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConsecutiveErrors); val.IsValid() && !isEmptyValue(val) { + transformed["consecutiveErrors"] = transformedConsecutiveErrors + } + + transformedConsecutiveGatewayFailure, err := expandComputeRegionBackendServiceOutlierDetectionConsecutiveGatewayFailure(original["consecutive_gateway_failure"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConsecutiveGatewayFailure); val.IsValid() && !isEmptyValue(val) { + transformed["consecutiveGatewayFailure"] = transformedConsecutiveGatewayFailure + } + + transformedEnforcingConsecutiveErrors, err := expandComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveErrors(original["enforcing_consecutive_errors"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingConsecutiveErrors); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingConsecutiveErrors"] = transformedEnforcingConsecutiveErrors + } + + transformedEnforcingConsecutiveGatewayFailure, err := expandComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(original["enforcing_consecutive_gateway_failure"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingConsecutiveGatewayFailure); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingConsecutiveGatewayFailure"] = transformedEnforcingConsecutiveGatewayFailure + } + + transformedEnforcingSuccessRate, err := expandComputeRegionBackendServiceOutlierDetectionEnforcingSuccessRate(original["enforcing_success_rate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnforcingSuccessRate); val.IsValid() && !isEmptyValue(val) { + transformed["enforcingSuccessRate"] = transformedEnforcingSuccessRate + } + + transformedInterval, err := expandComputeRegionBackendServiceOutlierDetectionInterval(original["interval"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInterval); val.IsValid() && !isEmptyValue(val) { + transformed["interval"] = transformedInterval + } + + transformedMaxEjectionPercent, err := expandComputeRegionBackendServiceOutlierDetectionMaxEjectionPercent(original["max_ejection_percent"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxEjectionPercent); val.IsValid() && !isEmptyValue(val) { + transformed["maxEjectionPercent"] = transformedMaxEjectionPercent + } + + transformedSuccessRateMinimumHosts, err := expandComputeRegionBackendServiceOutlierDetectionSuccessRateMinimumHosts(original["success_rate_minimum_hosts"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateMinimumHosts); val.IsValid() && !isEmptyValue(val) { + transformed["successRateMinimumHosts"] = transformedSuccessRateMinimumHosts + } + + transformedSuccessRateRequestVolume, err := expandComputeRegionBackendServiceOutlierDetectionSuccessRateRequestVolume(original["success_rate_request_volume"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateRequestVolume); val.IsValid() && !isEmptyValue(val) { + transformed["successRateRequestVolume"] = transformedSuccessRateRequestVolume + } + + transformedSuccessRateStdevFactor, err := expandComputeRegionBackendServiceOutlierDetectionSuccessRateStdevFactor(original["success_rate_stdev_factor"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuccessRateStdevFactor); val.IsValid() && !isEmptyValue(val) { + transformed["successRateStdevFactor"] = transformedSuccessRateStdevFactor + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTime(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionBaseEjectionTimeNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionConsecutiveErrors(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionConsecutiveGatewayFailure(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveErrors(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionEnforcingConsecutiveGatewayFailure(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionEnforcingSuccessRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionInterval(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSeconds, err := expandComputeRegionBackendServiceOutlierDetectionIntervalSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandComputeRegionBackendServiceOutlierDetectionIntervalNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionIntervalSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionIntervalNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionMaxEjectionPercent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionSuccessRateMinimumHosts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionSuccessRateRequestVolume(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceOutlierDetectionSuccessRateStdevFactor(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeRegionBackendServiceProtocol(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1153,6 +2691,40 @@ func expandComputeRegionBackendServiceTimeoutSec(v interface{}, d TerraformResou return v, nil } +func expandComputeRegionBackendServiceLogConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnable, err := expandComputeRegionBackendServiceLogConfigEnable(original["enable"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnable); val.IsValid() && !isEmptyValue(val) { + transformed["enable"] = transformedEnable + } + + transformedSampleRate, err := expandComputeRegionBackendServiceLogConfigSampleRate(original["sample_rate"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSampleRate); val.IsValid() && !isEmptyValue(val) { + transformed["sampleRate"] = transformedSampleRate + } + + return transformed, nil +} + +func expandComputeRegionBackendServiceLogConfigEnable(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionBackendServiceLogConfigSampleRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeRegionBackendServiceNetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { f, err := parseGlobalFieldValue("networks", v.(string), "project", d, config, true) if err != nil { diff --git a/google/resource_compute_region_backend_service_generated_test.go b/google/resource_compute_region_backend_service_generated_test.go index 6bd9a1253c4..8c46f7a35d6 100644 --- a/google/resource_compute_region_backend_service_generated_test.go +++ b/google/resource_compute_region_backend_service_generated_test.go @@ -69,6 +69,210 @@ resource "google_compute_health_check" "default" { `, context) } +func TestAccComputeRegionBackendService_regionBackendServiceIlbRoundRobinExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionBackendService_regionBackendServiceIlbRoundRobinExample(context), + }, + { + ResourceName: "google_compute_region_backend_service.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionBackendService_regionBackendServiceIlbRoundRobinExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "tf-test-region-service%{random_suffix}" + health_checks = ["${google_compute_health_check.health_check.self_link}"] + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + locality_lb_policy = "ROUND_ROBIN" +} + +resource "google_compute_health_check" "health_check" { + name = "tf-test-rbs-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionBackendService_regionBackendServiceIlbRingHashExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionBackendService_regionBackendServiceIlbRingHashExample(context), + }, + { + ResourceName: "google_compute_region_backend_service.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionBackendService_regionBackendServiceIlbRingHashExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "tf-test-region-service%{random_suffix}" + health_checks = ["${google_compute_health_check.health_check.self_link}"] + load_balancing_scheme = "INTERNAL_MANAGED" + locality_lb_policy = "RING_HASH" + session_affinity = "HTTP_COOKIE" + protocol = "HTTP" + circuit_breakers { + max_connections = 10 + } + consistent_hash { + http_cookie { + ttl { + seconds = 11 + nanos = 1111 + } + name = "mycookie" + } + } + outlier_detection { + consecutive_errors = 2 + } +} + +resource "google_compute_health_check" "health_check" { + name = "tf-test-rbs-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionBackendService_regionBackendServiceBalancingModeExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionBackendService_regionBackendServiceBalancingModeExample(context), + }, + { + ResourceName: "google_compute_region_backend_service.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionBackendService_regionBackendServiceBalancingModeExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_backend_service" "default" { + load_balancing_scheme = "INTERNAL_MANAGED" + + backend { + group = google_compute_region_instance_group_manager.rigm.instance_group + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } + + region = "us-central1" + name = "tf-test-region-service%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +data "google_compute_image" "debian_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_region_instance_group_manager" "rigm" { + region = "us-central1" + name = "tf-test-rbs-rigm%{random_suffix}" + version { + instance_template = google_compute_instance_template.instance_template.self_link + name = "primary" + } + base_instance_name = "internal-glb" + target_size = 1 +} + +resource "google_compute_instance_template" "instance_template" { + name = "template-tf-test-region-service%{random_suffix}" + machine_type = "n1-standard-1" + + network_interface { + network = google_compute_network.default.self_link + subnetwork = google_compute_subnetwork.default.self_link + } + + disk { + source_image = data.google_compute_image.debian_image.self_link + auto_delete = true + boot = true + } + + tags = ["allow-ssh", "load-balanced-backend"] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "tf-test-rbs-health-check%{random_suffix}" + http_health_check { + port_specification = "USE_SERVING_PORT" + } +} + +resource "google_compute_network" "default" { + name = "tf-test-rbs-net%{random_suffix}" + auto_create_subnetworks = false + routing_mode = "REGIONAL" +} + +resource "google_compute_subnetwork" "default" { + name = "tf-test-rbs-net%{random_suffix}-default" + ip_cidr_range = "10.1.2.0/24" + region = "us-central1" + network = google_compute_network.default.self_link +} +`, context) +} + func testAccCheckComputeRegionBackendServiceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/google/resource_compute_region_backend_service_test.go b/google/resource_compute_region_backend_service_test.go index 284b48de724..d776a44c4a6 100644 --- a/google/resource_compute_region_backend_service_test.go +++ b/google/resource_compute_region_backend_service_test.go @@ -169,6 +169,96 @@ func TestAccComputeRegionBackendService_withConnectionDrainingAndUpdate(t *testi }) } +func TestAccComputeRegionBackendService_ilbUpdateBasic(t *testing.T) { + t.Parallel() + + backendName := fmt.Sprintf("foo-%s", randString(t, 10)) + checkName := fmt.Sprintf("bar-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionBackendService_ilbBasic(backendName, checkName), + }, + { + ResourceName: "google_compute_region_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionBackendService_ilbUpdateBasic(backendName, checkName), + }, + { + ResourceName: "google_compute_region_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionBackendService_ilbBasic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_health_check.health_check.self_link] + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + locality_lb_policy = "RING_HASH" + circuit_breakers { + max_connections = 10 + } + consistent_hash { + http_cookie { + ttl { + seconds = 11 + nanos = 1234 + } + name = "mycookie" + } + } + outlier_detection { + consecutive_errors = 2 + } +} + +resource "google_compute_health_check" "health_check" { + name = "%s" + http_health_check { + port = 80 + } +} +`, serviceName, checkName) +} + +func testAccComputeRegionBackendService_ilbUpdateBasic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_health_check.health_check.self_link] + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + locality_lb_policy = "RANDOM" + circuit_breakers { + max_connections = 10 + } + outlier_detection { + consecutive_errors = 2 + } +} + +resource "google_compute_health_check" "health_check" { + name = "%s" + http_health_check { + port = 80 + } +} +`, serviceName, checkName) +} + func testAccComputeRegionBackendService_basic(serviceName, checkName string) string { return fmt.Sprintf(` resource "google_compute_region_backend_service" "foobar" { diff --git a/google/resource_compute_region_ssl_certificate_generated_test.go b/google/resource_compute_region_ssl_certificate_generated_test.go index 224b05833ee..1d3f2a0ede4 100644 --- a/google/resource_compute_region_ssl_certificate_generated_test.go +++ b/google/resource_compute_region_ssl_certificate_generated_test.go @@ -119,6 +119,103 @@ resource "random_id" "certificate" { `, context) } +func TestAccComputeRegionSslCertificate_regionSslCertificateTargetHttpsProxiesExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionSslCertificateDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionSslCertificate_regionSslCertificateTargetHttpsProxiesExample(context), + }, + { + ResourceName: "google_compute_region_ssl_certificate.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"private_key", "name_prefix"}, + }, + }, + }) +} + +func testAccComputeRegionSslCertificate_regionSslCertificateTargetHttpsProxiesExample(context map[string]interface{}) string { + return Nprintf(` +// Using with Region Target HTTPS Proxies +// +// SSL certificates cannot be updated after creation. In order to apply +// the specified configuration, Terraform will destroy the existing +// resource and create a replacement. To effectively use an SSL +// certificate resource with a Target HTTPS Proxy resource, it's +// recommended to specify create_before_destroy in a lifecycle block. +// Either omit the Instance Template name attribute, specify a partial +// name with name_prefix, or use random_id resource. Example: + +resource "google_compute_region_ssl_certificate" "default" { + region = "us-central1" + name_prefix = "my-certificate-" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_region_target_https_proxy" "default" { + region = "us-central1" + name = "tf-test-test-proxy%{random_suffix}" + url_map = google_compute_region_url_map.default.self_link + ssl_certificates = [google_compute_region_ssl_certificate.default.self_link] +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "tf-test-url-map%{random_suffix}" + description = "a description" + + default_service = google_compute_region_backend_service.default.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.default.self_link + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.default.self_link + } + } +} + +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "tf-test-backend-service%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "tf-test-http-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + func testAccCheckComputeRegionSslCertificateDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/google/resource_compute_region_target_http_proxy.go b/google/resource_compute_region_target_http_proxy.go new file mode 100644 index 00000000000..6a2d66daab3 --- /dev/null +++ b/google/resource_compute_region_target_http_proxy.go @@ -0,0 +1,378 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceComputeRegionTargetHttpProxy() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeRegionTargetHttpProxyCreate, + Read: resourceComputeRegionTargetHttpProxyRead, + Update: resourceComputeRegionTargetHttpProxyUpdate, + Delete: resourceComputeRegionTargetHttpProxyDelete, + + Importer: &schema.ResourceImporter{ + State: resourceComputeRegionTargetHttpProxyImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(4 * time.Minute), + Update: schema.DefaultTimeout(4 * time.Minute), + Delete: schema.DefaultTimeout(4 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of the resource. Provided by the client when the resource is +created. The name must be 1-63 characters long, and comply with +RFC1035. Specifically, the name must be 1-63 characters long and match +the regular expression '[a-z]([-a-z0-9]*[a-z0-9])?' which means the +first character must be a lowercase letter, and all following +characters must be a dash, lowercase letter, or digit, except the last +character, which cannot be a dash.`, + }, + "url_map": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `A reference to the RegionUrlMap resource that defines the mapping from URL +to the BackendService.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `An optional description of this resource.`, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The Region in which the created target https proxy should reside. +If it is not provided, the provider region is used.`, + }, + "creation_timestamp": { + Type: schema.TypeString, + Computed: true, + Description: `Creation timestamp in RFC3339 text format.`, + }, + "proxy_id": { + Type: schema.TypeInt, + Computed: true, + Description: `The unique identifier for the resource.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "self_link": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceComputeRegionTargetHttpProxyCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + descriptionProp, err := expandComputeRegionTargetHttpProxyDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + nameProp, err := expandComputeRegionTargetHttpProxyName(d.Get("name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + urlMapProp, err := expandComputeRegionTargetHttpProxyUrlMap(d.Get("url_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("url_map"); !isEmptyValue(reflect.ValueOf(urlMapProp)) && (ok || !reflect.DeepEqual(v, urlMapProp)) { + obj["urlMap"] = urlMapProp + } + regionProp, err := expandComputeRegionTargetHttpProxyRegion(d.Get("region"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(regionProp)) && (ok || !reflect.DeepEqual(v, regionProp)) { + obj["region"] = regionProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpProxies") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new RegionTargetHttpProxy: %#v", obj) + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating RegionTargetHttpProxy: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = computeOperationWaitTime( + config, res, project, "Creating RegionTargetHttpProxy", + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create RegionTargetHttpProxy: %s", err) + } + + log.Printf("[DEBUG] Finished creating RegionTargetHttpProxy %q: %#v", d.Id(), res) + + return resourceComputeRegionTargetHttpProxyRead(d, meta) +} + +func resourceComputeRegionTargetHttpProxyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}") + if err != nil { + return err + } + + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("ComputeRegionTargetHttpProxy %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + + if err := d.Set("creation_timestamp", flattenComputeRegionTargetHttpProxyCreationTimestamp(res["creationTimestamp"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("description", flattenComputeRegionTargetHttpProxyDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("proxy_id", flattenComputeRegionTargetHttpProxyProxyId(res["id"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("name", flattenComputeRegionTargetHttpProxyName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("url_map", flattenComputeRegionTargetHttpProxyUrlMap(res["urlMap"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("region", flattenComputeRegionTargetHttpProxyRegion(res["region"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpProxy: %s", err) + } + + return nil +} + +func resourceComputeRegionTargetHttpProxyUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + d.Partial(true) + + if d.HasChange("url_map") { + obj := make(map[string]interface{}) + + urlMapProp, err := expandComputeRegionTargetHttpProxyUrlMap(d.Get("url_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("url_map"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, urlMapProp)) { + obj["urlMap"] = urlMapProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}/setUrlMap") + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error updating RegionTargetHttpProxy %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Updating RegionTargetHttpProxy", + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + + d.SetPartial("url_map") + } + + d.Partial(false) + + return resourceComputeRegionTargetHttpProxyRead(d, meta) +} + +func resourceComputeRegionTargetHttpProxyDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting RegionTargetHttpProxy %q", d.Id()) + + res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "RegionTargetHttpProxy") + } + + err = computeOperationWaitTime( + config, res, project, "Deleting RegionTargetHttpProxy", + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting RegionTargetHttpProxy %q: %#v", d.Id(), res) + return nil +} + +func resourceComputeRegionTargetHttpProxyImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/regions/(?P[^/]+)/targetHttpProxies/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenComputeRegionTargetHttpProxyCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpProxyDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpProxyProxyId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionTargetHttpProxyName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpProxyUrlMap(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionTargetHttpProxyRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return NameFromSelfLinkStateFunc(v) +} + +func expandComputeRegionTargetHttpProxyDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionTargetHttpProxyName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionTargetHttpProxyUrlMap(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("urlMaps", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for url_map: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionTargetHttpProxyRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for region: %s", err) + } + return f.RelativeLink(), nil +} diff --git a/google/resource_compute_region_target_http_proxy_generated_test.go b/google/resource_compute_region_target_http_proxy_generated_test.go new file mode 100644 index 00000000000..1e2e38277e2 --- /dev/null +++ b/google/resource_compute_region_target_http_proxy_generated_test.go @@ -0,0 +1,165 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccComputeRegionTargetHttpProxy_regionTargetHttpProxyBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionTargetHttpProxyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionTargetHttpProxy_regionTargetHttpProxyBasicExample(context), + }, + { + ResourceName: "google_compute_region_target_http_proxy.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionTargetHttpProxy_regionTargetHttpProxyBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_target_http_proxy" "default" { + region = "us-central1" + name = "tf-test-test-proxy%{random_suffix}" + url_map = google_compute_region_url_map.default.self_link +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "tf-test-url-map%{random_suffix}" + default_service = google_compute_region_backend_service.default.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.default.self_link + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.default.self_link + } + } +} + +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "tf-test-backend-service%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "tf-test-http-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionTargetHttpProxy_regionTargetHttpProxyHttpsRedirectExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionTargetHttpProxyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionTargetHttpProxy_regionTargetHttpProxyHttpsRedirectExample(context), + }, + { + ResourceName: "google_compute_region_target_http_proxy.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionTargetHttpProxy_regionTargetHttpProxyHttpsRedirectExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_target_http_proxy" "default" { + region = "us-central1" + name = "tf-test-test-https-redirect-proxy%{random_suffix}" + url_map = google_compute_region_url_map.default.self_link +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "tf-test-url-map%{random_suffix}" + default_url_redirect { + https_redirect = true + } +} +`, context) +} + +func testAccCheckComputeRegionTargetHttpProxyDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_target_http_proxy" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", "", url, nil) + if err == nil { + return fmt.Errorf("ComputeRegionTargetHttpProxy still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_compute_region_target_http_proxy_sweeper_test.go b/google/resource_compute_region_target_http_proxy_sweeper_test.go new file mode 100644 index 00000000000..a4969892745 --- /dev/null +++ b/google/resource_compute_region_target_http_proxy_sweeper_test.go @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func init() { + resource.AddTestSweepers("ComputeRegionTargetHttpProxy", &resource.Sweeper{ + Name: "ComputeRegionTargetHttpProxy", + F: testSweepComputeRegionTargetHttpProxy, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepComputeRegionTargetHttpProxy(region string) error { + resourceName := "ComputeRegionTargetHttpProxy" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/targetHttpProxies", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["items"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/resource_compute_region_target_http_proxy_test.go b/google/resource_compute_region_target_http_proxy_test.go index 71664db3c87..5d456bddd93 100644 --- a/google/resource_compute_region_target_http_proxy_test.go +++ b/google/resource_compute_region_target_http_proxy_test.go @@ -1 +1,176 @@ package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccComputeRegionTargetHttpProxy_update(t *testing.T) { + t.Parallel() + + target := fmt.Sprintf("thttp-test-%s", randString(t, 10)) + backend := fmt.Sprintf("thttp-test-%s", randString(t, 10)) + hc := fmt.Sprintf("thttp-test-%s", randString(t, 10)) + urlmap1 := fmt.Sprintf("thttp-test-%s", randString(t, 10)) + urlmap2 := fmt.Sprintf("thttp-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetHttpProxyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionTargetHttpProxy_basic1(target, backend, hc, urlmap1, urlmap2), + }, + { + ResourceName: "google_compute_region_target_http_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionTargetHttpProxy_basic2(target, backend, hc, urlmap1, urlmap2), + }, + { + ResourceName: "google_compute_region_target_http_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionTargetHttpProxy_basic1(target, backend, hc, urlmap1, urlmap2 string) string { + return fmt.Sprintf(` +resource "google_compute_region_target_http_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = google_compute_region_url_map.foobar1.self_link +} + +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_region_health_check.zero.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_health_check" "zero" { + name = "%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_url_map" "foobar1" { + name = "%s" + default_service = google_compute_region_backend_service.foobar.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} + +resource "google_compute_region_url_map" "foobar2" { + name = "%s" + default_service = google_compute_region_backend_service.foobar.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} +`, target, backend, hc, urlmap1, urlmap2) +} + +func testAccComputeRegionTargetHttpProxy_basic2(target, backend, hc, urlmap1, urlmap2 string) string { + return fmt.Sprintf(` +resource "google_compute_region_target_http_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = google_compute_region_url_map.foobar2.self_link +} + +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = [google_compute_region_health_check.zero.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_health_check" "zero" { + name = "%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_url_map" "foobar1" { + name = "%s" + default_service = google_compute_region_backend_service.foobar.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} + +resource "google_compute_region_url_map" "foobar2" { + name = "%s" + default_service = google_compute_region_backend_service.foobar.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} +`, target, backend, hc, urlmap1, urlmap2) +} diff --git a/google/resource_compute_region_target_https_proxy.go b/google/resource_compute_region_target_https_proxy.go new file mode 100644 index 00000000000..a745b5f6ac8 --- /dev/null +++ b/google/resource_compute_region_target_https_proxy.go @@ -0,0 +1,449 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceComputeRegionTargetHttpsProxy() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeRegionTargetHttpsProxyCreate, + Read: resourceComputeRegionTargetHttpsProxyRead, + Update: resourceComputeRegionTargetHttpsProxyUpdate, + Delete: resourceComputeRegionTargetHttpsProxyDelete, + + Importer: &schema.ResourceImporter{ + State: resourceComputeRegionTargetHttpsProxyImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(4 * time.Minute), + Update: schema.DefaultTimeout(4 * time.Minute), + Delete: schema.DefaultTimeout(4 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of the resource. Provided by the client when the resource is +created. The name must be 1-63 characters long, and comply with +RFC1035. Specifically, the name must be 1-63 characters long and match +the regular expression '[a-z]([-a-z0-9]*[a-z0-9])?' which means the +first character must be a lowercase letter, and all following +characters must be a dash, lowercase letter, or digit, except the last +character, which cannot be a dash.`, + }, + "ssl_certificates": { + Type: schema.TypeList, + Required: true, + Description: `A list of RegionSslCertificate resources that are used to authenticate +connections between users and the load balancer. Currently, exactly +one SSL certificate must be specified.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + }, + "url_map": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `A reference to the RegionUrlMap resource that defines the mapping from URL +to the RegionBackendService.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `An optional description of this resource.`, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The Region in which the created target https proxy should reside. +If it is not provided, the provider region is used.`, + }, + "creation_timestamp": { + Type: schema.TypeString, + Computed: true, + Description: `Creation timestamp in RFC3339 text format.`, + }, + "proxy_id": { + Type: schema.TypeInt, + Computed: true, + Description: `The unique identifier for the resource.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "self_link": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceComputeRegionTargetHttpsProxyCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + descriptionProp, err := expandComputeRegionTargetHttpsProxyDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + nameProp, err := expandComputeRegionTargetHttpsProxyName(d.Get("name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ssl_certificates"); !isEmptyValue(reflect.ValueOf(sslCertificatesProp)) && (ok || !reflect.DeepEqual(v, sslCertificatesProp)) { + obj["sslCertificates"] = sslCertificatesProp + } + urlMapProp, err := expandComputeRegionTargetHttpsProxyUrlMap(d.Get("url_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("url_map"); !isEmptyValue(reflect.ValueOf(urlMapProp)) && (ok || !reflect.DeepEqual(v, urlMapProp)) { + obj["urlMap"] = urlMapProp + } + regionProp, err := expandComputeRegionTargetHttpsProxyRegion(d.Get("region"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(regionProp)) && (ok || !reflect.DeepEqual(v, regionProp)) { + obj["region"] = regionProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new RegionTargetHttpsProxy: %#v", obj) + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating RegionTargetHttpsProxy: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = computeOperationWaitTime( + config, res, project, "Creating RegionTargetHttpsProxy", + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create RegionTargetHttpsProxy: %s", err) + } + + log.Printf("[DEBUG] Finished creating RegionTargetHttpsProxy %q: %#v", d.Id(), res) + + return resourceComputeRegionTargetHttpsProxyRead(d, meta) +} + +func resourceComputeRegionTargetHttpsProxyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}") + if err != nil { + return err + } + + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("ComputeRegionTargetHttpsProxy %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + + if err := d.Set("creation_timestamp", flattenComputeRegionTargetHttpsProxyCreationTimestamp(res["creationTimestamp"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("description", flattenComputeRegionTargetHttpsProxyDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("proxy_id", flattenComputeRegionTargetHttpsProxyProxyId(res["id"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("name", flattenComputeRegionTargetHttpsProxyName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("ssl_certificates", flattenComputeRegionTargetHttpsProxySslCertificates(res["sslCertificates"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("url_map", flattenComputeRegionTargetHttpsProxyUrlMap(res["urlMap"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("region", flattenComputeRegionTargetHttpsProxyRegion(res["region"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { + return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err) + } + + return nil +} + +func resourceComputeRegionTargetHttpsProxyUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + d.Partial(true) + + if d.HasChange("ssl_certificates") { + obj := make(map[string]interface{}) + + sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ssl_certificates"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, sslCertificatesProp)) { + obj["sslCertificates"] = sslCertificatesProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}/setSslCertificates") + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error updating RegionTargetHttpsProxy %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Updating RegionTargetHttpsProxy", + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + + d.SetPartial("ssl_certificates") + } + if d.HasChange("url_map") { + obj := make(map[string]interface{}) + + urlMapProp, err := expandComputeRegionTargetHttpsProxyUrlMap(d.Get("url_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("url_map"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, urlMapProp)) { + obj["urlMap"] = urlMapProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}/setUrlMap") + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error updating RegionTargetHttpsProxy %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Updating RegionTargetHttpsProxy", + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + + d.SetPartial("url_map") + } + + d.Partial(false) + + return resourceComputeRegionTargetHttpsProxyRead(d, meta) +} + +func resourceComputeRegionTargetHttpsProxyDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting RegionTargetHttpsProxy %q", d.Id()) + + res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "RegionTargetHttpsProxy") + } + + err = computeOperationWaitTime( + config, res, project, "Deleting RegionTargetHttpsProxy", + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting RegionTargetHttpsProxy %q: %#v", d.Id(), res) + return nil +} + +func resourceComputeRegionTargetHttpsProxyImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/regions/(?P[^/]+)/targetHttpsProxies/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenComputeRegionTargetHttpsProxyCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpsProxyDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpsProxyProxyId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionTargetHttpsProxyName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionTargetHttpsProxySslCertificates(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) +} + +func flattenComputeRegionTargetHttpsProxyUrlMap(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionTargetHttpsProxyRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return NameFromSelfLinkStateFunc(v) +} + +func expandComputeRegionTargetHttpsProxyDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionTargetHttpsProxyName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionTargetHttpsProxySslCertificates(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + return nil, fmt.Errorf("Invalid value for ssl_certificates: nil") + } + f, err := parseRegionalFieldValue("sslCertificates", raw.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for ssl_certificates: %s", err) + } + req = append(req, f.RelativeLink()) + } + return req, nil +} + +func expandComputeRegionTargetHttpsProxyUrlMap(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("urlMaps", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for url_map: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionTargetHttpsProxyRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for region: %s", err) + } + return f.RelativeLink(), nil +} diff --git a/google/resource_compute_region_target_https_proxy_generated_test.go b/google/resource_compute_region_target_https_proxy_generated_test.go new file mode 100644 index 00000000000..54f1c6877e6 --- /dev/null +++ b/google/resource_compute_region_target_https_proxy_generated_test.go @@ -0,0 +1,133 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyBasicExample(context), + }, + { + ResourceName: "google_compute_region_target_https_proxy.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_target_https_proxy" "default" { + region = "us-central1" + name = "tf-test-test-proxy%{random_suffix}" + url_map = google_compute_region_url_map.default.self_link + ssl_certificates = [google_compute_region_ssl_certificate.default.self_link] +} + +resource "google_compute_region_ssl_certificate" "default" { + region = "us-central1" + name = "tf-test-my-certificate%{random_suffix}" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "tf-test-url-map%{random_suffix}" + description = "a description" + + default_service = google_compute_region_backend_service.default.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.default.self_link + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.default.self_link + } + } +} + +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "tf-test-backend-service%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "tf-test-http-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_target_https_proxy" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", "", url, nil) + if err == nil { + return fmt.Errorf("ComputeRegionTargetHttpsProxy still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_compute_region_target_https_proxy_sweeper_test.go b/google/resource_compute_region_target_https_proxy_sweeper_test.go new file mode 100644 index 00000000000..eeb6acae8bc --- /dev/null +++ b/google/resource_compute_region_target_https_proxy_sweeper_test.go @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func init() { + resource.AddTestSweepers("ComputeRegionTargetHttpsProxy", &resource.Sweeper{ + Name: "ComputeRegionTargetHttpsProxy", + F: testSweepComputeRegionTargetHttpsProxy, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepComputeRegionTargetHttpsProxy(region string) error { + resourceName := "ComputeRegionTargetHttpsProxy" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/targetHttpsProxies", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["items"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/resource_compute_region_target_https_proxy_test.go b/google/resource_compute_region_target_https_proxy_test.go index 71664db3c87..33dabeb43f1 100644 --- a/google/resource_compute_region_target_https_proxy_test.go +++ b/google/resource_compute_region_target_https_proxy_test.go @@ -1 +1,231 @@ package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccComputeRegionTargetHttpsProxy_update(t *testing.T) { + t.Parallel() + + resourceSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetHttpsProxyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionTargetHttpsProxy_basic1(resourceSuffix), + }, + { + ResourceName: "google_compute_region_target_https_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionTargetHttpsProxy_basic2(resourceSuffix), + }, + { + ResourceName: "google_compute_region_target_https_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionTargetHttpsProxy_basic1(id string) string { + return fmt.Sprintf(` +resource "google_compute_region_target_https_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "httpsproxy-test-%s" + url_map = google_compute_region_url_map.foobar1.self_link + ssl_certificates = [google_compute_region_ssl_certificate.foobar1.self_link] +} + +resource "google_compute_region_backend_service" "foobar1" { + name = "httpsproxy-test-backend1-%s" + health_checks = [google_compute_region_health_check.zero.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_backend_service" "foobar2" { + name = "httpsproxy-test-backend2-%s" + health_checks = [google_compute_region_health_check.one.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_health_check" "zero" { + name = "httpsproxy-test-health-check1-%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_health_check" "one" { + name = "httpsproxy-test-health-check2-%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_url_map" "foobar1" { + name = "httpsproxy-test-url-map1-%s" + default_service = google_compute_region_backend_service.foobar1.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar1.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar1.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar1.self_link + } +} + +resource "google_compute_region_url_map" "foobar2" { + name = "httpsproxy-test-url-map2-%s" + default_service = google_compute_region_backend_service.foobar2.self_link + host_rule { + hosts = ["mysite2.com", "myothersite2.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar2.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar2.self_link + } + } + test { + host = "mysite2.com" + path = "/*" + service = google_compute_region_backend_service.foobar2.self_link + } +} + +resource "google_compute_region_ssl_certificate" "foobar1" { + name = "httpsproxy-test-cert1-%s" + description = "very descriptive" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") +} + +resource "google_compute_region_ssl_certificate" "foobar2" { + name = "httpsproxy-test-cert2-%s" + description = "very descriptive" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") +} +`, id, id, id, id, id, id, id, id, id) +} + +func testAccComputeRegionTargetHttpsProxy_basic2(id string) string { + return fmt.Sprintf(` +resource "google_compute_region_target_https_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "httpsproxy-test-%s" + url_map = google_compute_region_url_map.foobar2.self_link + ssl_certificates = [ + google_compute_region_ssl_certificate.foobar1.self_link, + google_compute_region_ssl_certificate.foobar2.self_link, + ] +} + +resource "google_compute_region_backend_service" "foobar1" { + name = "httpsproxy-test-backend1-%s" + health_checks = [google_compute_region_health_check.zero.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_backend_service" "foobar2" { + name = "httpsproxy-test-backend2-%s" + health_checks = [google_compute_region_health_check.one.self_link] + protocol = "HTTP" +} + +resource "google_compute_region_health_check" "zero" { + name = "httpsproxy-test-health-check1-%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_health_check" "one" { + name = "httpsproxy-test-health-check2-%s" + http_health_check { + port = 443 + } +} + +resource "google_compute_region_url_map" "foobar1" { + name = "httpsproxy-test-url-map1-%s" + default_service = google_compute_region_backend_service.foobar1.self_link + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar1.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar1.self_link + } + } + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar1.self_link + } +} + +resource "google_compute_region_url_map" "foobar2" { + name = "httpsproxy-test-url-map2-%s" + default_service = google_compute_region_backend_service.foobar2.self_link + host_rule { + hosts = ["mysite2.com", "myothersite2.com"] + path_matcher = "boop" + } + path_matcher { + default_service = google_compute_region_backend_service.foobar2.self_link + name = "boop" + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar2.self_link + } + } + test { + host = "mysite2.com" + path = "/*" + service = google_compute_region_backend_service.foobar2.self_link + } +} + +resource "google_compute_region_ssl_certificate" "foobar1" { + name = "httpsproxy-test-cert1-%s" + description = "very descriptive" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") +} + +resource "google_compute_region_ssl_certificate" "foobar2" { + name = "httpsproxy-test-cert2-%s" + description = "very descriptive" + private_key = file("test-fixtures/ssl_cert/test.key") + certificate = file("test-fixtures/ssl_cert/test.crt") +} +`, id, id, id, id, id, id, id, id, id) +} diff --git a/google/resource_compute_region_url_map.go b/google/resource_compute_region_url_map.go new file mode 100644 index 00000000000..965aa536820 --- /dev/null +++ b/google/resource_compute_region_url_map.go @@ -0,0 +1,6179 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceComputeRegionUrlMap() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeRegionUrlMapCreate, + Read: resourceComputeRegionUrlMapRead, + Update: resourceComputeRegionUrlMapUpdate, + Delete: resourceComputeRegionUrlMapDelete, + + Importer: &schema.ResourceImporter{ + State: resourceComputeRegionUrlMapImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(4 * time.Minute), + Update: schema.DefaultTimeout(4 * time.Minute), + Delete: schema.DefaultTimeout(4 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of the resource. Provided by the client when the resource is +created. The name must be 1-63 characters long, and comply with +RFC1035. Specifically, the name must be 1-63 characters long and match +the regular expression '[a-z]([-a-z0-9]*[a-z0-9])?' which means the +first character must be a lowercase letter, and all following +characters must be a dash, lowercase letter, or digit, except the last +character, which cannot be a dash.`, + }, + "default_service": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The full or partial URL of the defaultService resource to which traffic is directed if +none of the hostRules match. If defaultRouteAction is additionally specified, advanced +routing actions like URL Rewrites, etc. take effect prior to sending the request to the +backend. However, if defaultService is specified, defaultRouteAction cannot contain any +weightedBackendServices. Conversely, if routeAction specifies any +weightedBackendServices, service must not be specified. Only one of defaultService, +defaultUrlRedirect or defaultRouteAction.weightedBackendService must be set.`, + ExactlyOneOf: []string{"default_service", "default_url_redirect"}, + }, + "default_url_redirect": { + Type: schema.TypeList, + Optional: true, + Description: `When none of the specified hostRules match, the request is redirected to a URL specified +by defaultUrlRedirect. If defaultUrlRedirect is specified, defaultService or +defaultRouteAction must not be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The host that will be used in the redirect response instead of the one that was +supplied in the request. The value must be between 1 and 255 characters.`, + }, + "https_redirect": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, the URL scheme in the redirected request is set to https. If set to +false, the URL scheme of the redirected request will remain the same as that of the +request. This must only be set for UrlMaps used in TargetHttpProxys. Setting this +true for TargetHttpsProxy is not permitted. The default is set to false.`, + Default: false, + }, + "path_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The path that will be used in the redirect response instead of the one that was +supplied in the request. pathRedirect cannot be supplied together with +prefixRedirect. Supply one alone or neither. If neither is supplied, the path of the +original request will be used for the redirect. The value must be between 1 and 1024 +characters.`, + }, + "prefix_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The prefix that replaces the prefixMatch specified in the HttpRouteRuleMatch, +retaining the remaining portion of the URL before redirecting the request. +prefixRedirect cannot be supplied together with pathRedirect. Supply one alone or +neither. If neither is supplied, the path of the original request will be used for +the redirect. The value must be between 1 and 1024 characters.`, + }, + "redirect_response_code": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT", ""}, false), + Description: `The HTTP Status code to use for this RedirectAction. Supported values are: +- MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. +- FOUND, which corresponds to 302. +- SEE_OTHER which corresponds to 303. +- TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method +will be retained. +- PERMANENT_REDIRECT, which corresponds to 308. In this case, +the request method will be retained. Possible values: ["FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT"]`, + }, + "strip_query": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, any accompanying query portion of the original URL is removed prior +to redirecting the request. If set to false, the query portion of the original URL is +retained. The default is set to false.`, + Default: false, + }, + }, + }, + ExactlyOneOf: []string{"default_service", "default_url_redirect"}, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `An optional description of this resource. Provide this property when +you create the resource.`, + }, + "host_rule": { + Type: schema.TypeSet, + Optional: true, + Description: `The list of HostRules to use against the URL.`, + Elem: computeRegionUrlMapHostRuleSchema(), + // Default schema.HashSchema is used. + }, + "path_matcher": { + Type: schema.TypeList, + Optional: true, + Description: `The list of named PathMatchers to use against the URL.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `A reference to a RegionBackendService resource. This will be used if +none of the pathRules defined by this PathMatcher is matched by +the URL's path portion.`, + ExactlyOneOf: []string{}, + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: `The name to which this PathMatcher is referred by the HostRule.`, + }, + "default_url_redirect": { + Type: schema.TypeList, + Optional: true, + Description: `When none of the specified hostRules match, the request is redirected to a URL specified +by defaultUrlRedirect. If defaultUrlRedirect is specified, defaultService or +defaultRouteAction must not be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The host that will be used in the redirect response instead of the one that was +supplied in the request. The value must be between 1 and 255 characters.`, + }, + "https_redirect": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, the URL scheme in the redirected request is set to https. If set to +false, the URL scheme of the redirected request will remain the same as that of the +request. This must only be set for UrlMaps used in TargetHttpProxys. Setting this +true for TargetHttpsProxy is not permitted. The default is set to false.`, + Default: false, + }, + "path_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The path that will be used in the redirect response instead of the one that was +supplied in the request. pathRedirect cannot be supplied together with +prefixRedirect. Supply one alone or neither. If neither is supplied, the path of the +original request will be used for the redirect. The value must be between 1 and 1024 +characters.`, + }, + "prefix_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The prefix that replaces the prefixMatch specified in the HttpRouteRuleMatch, +retaining the remaining portion of the URL before redirecting the request. +prefixRedirect cannot be supplied together with pathRedirect. Supply one alone or +neither. If neither is supplied, the path of the original request will be used for +the redirect. The value must be between 1 and 1024 characters.`, + }, + "redirect_response_code": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT", ""}, false), + Description: `The HTTP Status code to use for this RedirectAction. Supported values are: +- MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. +- FOUND, which corresponds to 302. +- SEE_OTHER which corresponds to 303. +- TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method +will be retained. +- PERMANENT_REDIRECT, which corresponds to 308. In this case, +the request method will be retained. Possible values: ["FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT"]`, + }, + "strip_query": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, any accompanying query portion of the original URL is removed prior +to redirecting the request. If set to false, the query portion of the original URL is +retained. The default is set to false.`, + Default: false, + }, + }, + }, + ExactlyOneOf: []string{}, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `An optional description of this resource.`, + }, + "path_rule": { + Type: schema.TypeList, + Optional: true, + Description: `The list of path rules. Use this list instead of routeRules when routing based +on simple path matching is all that's required. The order by which path rules +are specified does not matter. Matches are always done on the longest-path-first +basis. For example: a pathRule with a path /a/b/c/* will match before /a/b/* +irrespective of the order in which those paths appear in this list. Within a +given pathMatcher, only one of pathRules or routeRules must be set.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paths": { + Type: schema.TypeSet, + Required: true, + Description: `The list of path patterns to match. Each must start with / and the only place a +* is allowed is at the end following a /. The string fed to the path matcher +does not include any text after the first ? or #, and those chars are not +allowed here.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, + "route_action": { + Type: schema.TypeList, + Optional: true, + Description: `In response to a matching path, the load balancer performs advanced routing +actions like URL rewrites, header transformations, etc. prior to forwarding the +request to the selected backend. If routeAction specifies any +weightedBackendServices, service must not be set. Conversely if service is set, +routeAction cannot contain any weightedBackendServices. Only one of routeAction +or urlRedirect must be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cors_policy": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for allowing client side cross-origin requests. Please see W3C +Recommendation for Cross Origin Resource Sharing`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disabled": { + Type: schema.TypeBool, + Required: true, + Description: `If true, specifies the CORS policy is disabled.`, + }, + "allow_credentials": { + Type: schema.TypeBool, + Optional: true, + Description: `In response to a preflight request, setting this to true indicates that the +actual request can include user credentials. This translates to the Access- +Control-Allow-Credentials header. Defaults to false.`, + Default: false, + }, + "allow_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Headers header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_methods": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Methods header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_origin_regexes": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the regualar expression patterns that match allowed origins. For +regular expression grammar please see en.cppreference.com/w/cpp/regex/ecmascript +An origin is allowed if it matches either allow_origins or allow_origin_regex.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_origins": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the list of origins that will be allowed to do CORS requests. An +origin is allowed if it matches either allow_origins or allow_origin_regex.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "expose_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Expose-Headers header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "max_age": { + Type: schema.TypeInt, + Optional: true, + Description: `Specifies how long the results of a preflight request can be cached. This +translates to the content for the Access-Control-Max-Age header.`, + }, + }, + }, + }, + "fault_injection_policy": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for fault injection introduced into traffic to test the +resiliency of clients to backend service failure. As part of fault injection, +when clients send requests to a backend service, delays can be introduced by +Loadbalancer on a percentage of requests before sending those request to the +backend service. Similarly requests from clients can be aborted by the +Loadbalancer for a percentage of requests. timeout and retry_policy will be +ignored by clients that are configured with a fault_injection_policy.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "abort": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for how client requests are aborted as part of fault +injection.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_status": { + Type: schema.TypeInt, + Required: true, + Description: `The HTTP status code used to abort the request. The value must be between 200 +and 599 inclusive.`, + }, + "percentage": { + Type: schema.TypeFloat, + Required: true, + Description: `The percentage of traffic (connections/operations/requests) which will be +aborted as part of fault injection. The value must be between 0.0 and 100.0 +inclusive.`, + }, + }, + }, + }, + "delay": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for how client requests are delayed as part of fault +injection, before being sent to a backend service.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fixed_delay": { + Type: schema.TypeList, + Required: true, + Description: `Specifies the value of the fixed delay interval.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "percentage": { + Type: schema.TypeFloat, + Required: true, + Description: `The percentage of traffic (connections/operations/requests) on which delay will +be introduced as part of fault injection. The value must be between 0.0 and +100.0 inclusive.`, + }, + }, + }, + }, + }, + }, + }, + "request_mirror_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the policy on how requests intended for the route's backends are +shadowed to a separate mirrored backend service. Loadbalancer does not wait for +responses from the shadow service. Prior to sending traffic to the shadow +service, the host / authority header is suffixed with -shadow.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend_service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The RegionBackendService resource being mirrored to.`, + }, + }, + }, + }, + "retry_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the retry policy associated with this route.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "num_retries": { + Type: schema.TypeInt, + Optional: true, + Description: `Specifies the allowed number retries. This number must be > 0.`, + }, + "per_try_timeout": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies a non-zero timeout per retry attempt.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "retry_conditions": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies one or more conditions when this retry rule applies. Valid values are: +- 5xx: Loadbalancer will attempt a retry if the backend service responds with +any 5xx response code, or if the backend service does not respond at all, +example: disconnects, reset, read timeout, connection failure, and refused +streams. +- gateway-error: Similar to 5xx, but only applies to response codes +502, 503 or 504. +- connect-failure: Loadbalancer will retry on failures +connecting to backend services, for example due to connection timeouts. +- retriable-4xx: Loadbalancer will retry for retriable 4xx response codes. +Currently the only retriable error supported is 409. +- refused-stream: Loadbalancer will retry if the backend service resets the stream with a +REFUSED_STREAM error code. This reset type indicates that it is safe to retry. +- cancelled: Loadbalancer will retry if the gRPC status code in the response +header is set to cancelled +- deadline-exceeded: Loadbalancer will retry if the +gRPC status code in the response header is set to deadline-exceeded +- resource-exhausted: Loadbalancer will retry if the gRPC status code in the response +header is set to resource-exhausted +- unavailable: Loadbalancer will retry if +the gRPC status code in the response header is set to unavailable`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "timeout": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the timeout for the selected route. Timeout is computed from the time +the request is has been fully processed (i.e. end-of-stream) up until the +response has been completely processed. Timeout includes all retries. If not +specified, the default value is 15 seconds.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "url_rewrite": { + Type: schema.TypeList, + Optional: true, + Description: `The spec to modify the URL of the request, prior to forwarding the request to +the matched service`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected service, the request's host +header is replaced with contents of hostRewrite. The value must be between 1 and +255 characters.`, + }, + "path_prefix_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected backend service, the matching +portion of the request's path is replaced by pathPrefixRewrite. The value must +be between 1 and 1024 characters.`, + }, + }, + }, + }, + "weighted_backend_services": { + Type: schema.TypeList, + Optional: true, + Description: `A list of weighted backend services to send traffic to when a route match +occurs. The weights determine the fraction of traffic that flows to their +corresponding backend service. If all traffic needs to go to a single backend +service, there must be one weightedBackendService with weight set to a non 0 +number. Once a backendService is identified and before forwarding the request to +the backend service, advanced routing actions like Url rewrites and header +transformations are applied depending on additional settings specified in this +HttpRouteAction.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend_service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The default RegionBackendService resource. Before +forwarding the request to backendService, the loadbalancer applies any relevant +headerActions specified as part of this backendServiceWeight.`, + }, + "weight": { + Type: schema.TypeInt, + Required: true, + Description: `Specifies the fraction of traffic sent to backendService, computed as weight / +(sum of all weightedBackendService weights in routeAction) . The selection of a +backend service is determined only for new traffic. Once a user's request has +been directed to a backendService, subsequent requests will be sent to the same +backendService as determined by the BackendService's session affinity policy. +The value must be between 0 and 1000`, + }, + "header_action": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies changes to request and response headers that need to take effect for +the selected backendService. headerAction specified here take effect before +headerAction in the enclosing HttpRouteRule, PathMatcher and UrlMap.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add to a matching request prior to forwarding the request to the +backendService.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "request_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the request +prior to forwarding the request to the backendService.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "response_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add the response prior to sending the response back to the client.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "response_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the response +prior to sending the response back to the client.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "service": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The region backend service resource to which traffic is +directed if this rule is matched. If routeAction is additionally specified, +advanced routing actions like URL Rewrites, etc. take effect prior to sending +the request to the backend. However, if service is specified, routeAction cannot +contain any weightedBackendService s. Conversely, if routeAction specifies any +weightedBackendServices, service must not be specified. Only one of urlRedirect, +service or routeAction.weightedBackendService must be set.`, + }, + "url_redirect": { + Type: schema.TypeList, + Optional: true, + Description: `When a path pattern is matched, the request is redirected to a URL specified +by urlRedirect. If urlRedirect is specified, service or routeAction must not +be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The host that will be used in the redirect response instead of the one +that was supplied in the request. The value must be between 1 and 255 +characters.`, + }, + "https_redirect": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, the URL scheme in the redirected request is set to https. +If set to false, the URL scheme of the redirected request will remain the +same as that of the request. This must only be set for UrlMaps used in +TargetHttpProxys. Setting this true for TargetHttpsProxy is not +permitted. The default is set to false.`, + Default: false, + }, + "path_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The path that will be used in the redirect response instead of the one +that was supplied in the request. pathRedirect cannot be supplied +together with prefixRedirect. Supply one alone or neither. If neither is +supplied, the path of the original request will be used for the redirect. +The value must be between 1 and 1024 characters.`, + }, + "prefix_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The prefix that replaces the prefixMatch specified in the +HttpRouteRuleMatch, retaining the remaining portion of the URL before +redirecting the request. prefixRedirect cannot be supplied together with +pathRedirect. Supply one alone or neither. If neither is supplied, the +path of the original request will be used for the redirect. The value +must be between 1 and 1024 characters.`, + }, + "redirect_response_code": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT", ""}, false), + Description: `The HTTP Status code to use for this RedirectAction. Supported values are: +- MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. +- FOUND, which corresponds to 302. +- SEE_OTHER which corresponds to 303. +- TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method +will be retained. +- PERMANENT_REDIRECT, which corresponds to 308. In this case, +the request method will be retained. Possible values: ["FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT"]`, + }, + "strip_query": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, any accompanying query portion of the original URL is removed +prior to redirecting the request. If set to false, the query portion of the +original URL is retained.`, + Default: false, + }, + }, + }, + }, + }, + }, + }, + "route_rules": { + Type: schema.TypeList, + Optional: true, + Description: `The list of ordered HTTP route rules. Use this list instead of pathRules when +advanced route matching and routing actions are desired. The order of specifying +routeRules matters: the first rule that matches will cause its specified routing +action to take effect. Within a given pathMatcher, only one of pathRules or +routeRules must be set. routeRules are not supported in UrlMaps intended for +External load balancers.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "priority": { + Type: schema.TypeInt, + Required: true, + Description: `For routeRules within a given pathMatcher, priority determines the order +in which load balancer will interpret routeRules. RouteRules are evaluated +in order of priority, from the lowest to highest number. The priority of +a rule decreases as its number increases (1, 2, 3, N+1). The first rule +that matches the request is applied. + +You cannot configure two or more routeRules with the same priority. +Priority for each rule must be set to a number between 0 and +2147483647 inclusive. + +Priority numbers can have gaps, which enable you to add or remove rules +in the future without affecting the rest of the rules. For example, +1, 2, 3, 4, 5, 9, 12, 16 is a valid series of priority numbers to which +you could add rules numbered from 6 to 8, 10 to 11, and 13 to 15 in the +future without any impact on existing rules.`, + }, + "header_action": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies changes to request and response headers that need to take effect for +the selected backendService. The headerAction specified here are applied before +the matching pathMatchers[].headerAction and after pathMatchers[].routeRules[].r +outeAction.weightedBackendService.backendServiceWeightAction[].headerAction`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add to a matching request prior to forwarding the request to the +backendService.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "request_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the request +prior to forwarding the request to the backendService.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "response_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add the response prior to sending the response back to the client.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "response_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the response +prior to sending the response back to the client.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "match_rules": { + Type: schema.TypeList, + Optional: true, + Description: `The rules for determining a match.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "full_path_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satifying the matchRule condition, the path of the request must exactly +match the value specified in fullPathMatch after removing any query parameters +and anchor that may be part of the original URL. FullPathMatch must be between 1 +and 1024 characters. Only one of prefixMatch, fullPathMatch or regexMatch must +be specified.`, + }, + "header_matches": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies a list of header match criteria, all of which must match corresponding +headers in the request.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the HTTP header to match. For matching against the HTTP request's +authority, use a headerMatch with the header name ":authority". For matching a +request's method, use the headerName ":method".`, + }, + "exact_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value should exactly match contents of exactMatch. Only one of exactMatch, +prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch must be set.`, + }, + "invert_match": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to false, the headerMatch is considered a match if the match criteria +above are met. If set to true, the headerMatch is considered a match if the +match criteria above are NOT met. Defaults to false.`, + Default: false, + }, + "prefix_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header must start with the contents of prefixMatch. Only one of +exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch +must be set.`, + }, + "present_match": { + Type: schema.TypeBool, + Optional: true, + Description: `A header with the contents of headerName must exist. The match takes place +whether or not the request's header has a value or not. Only one of exactMatch, +prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch must be set.`, + }, + "range_match": { + Type: schema.TypeList, + Optional: true, + Description: `The header value must be an integer and its value must be in the range specified +in rangeMatch. If the header does not contain an integer, number or is empty, +the match fails. For example for a range [-5, 0] - -3 will match. - 0 will +not match. - 0.25 will not match. - -3someString will not match. Only one of +exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch +must be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "range_end": { + Type: schema.TypeInt, + Required: true, + Description: `The end of the range (exclusive).`, + }, + "range_start": { + Type: schema.TypeInt, + Required: true, + Description: `The start of the range (inclusive).`, + }, + }, + }, + }, + "regex_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header must match the regualar expression specified in +regexMatch. For regular expression grammar, please see: +en.cppreference.com/w/cpp/regex/ecmascript For matching against a port +specified in the HTTP request, use a headerMatch with headerName set to PORT and +a regular expression that satisfies the RFC2616 Host header's port specifier. +Only one of exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or +rangeMatch must be set.`, + }, + "suffix_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header must end with the contents of suffixMatch. Only one of +exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch +must be set.`, + }, + }, + }, + }, + "ignore_case": { + Type: schema.TypeBool, + Optional: true, + Description: `Specifies that prefixMatch and fullPathMatch matches are case sensitive. +Defaults to false.`, + Default: false, + }, + "metadata_filters": { + Type: schema.TypeList, + Optional: true, + Description: `Opaque filter criteria used by Loadbalancer to restrict routing configuration to +a limited set xDS compliant clients. In their xDS requests to Loadbalancer, xDS +clients present node metadata. If a match takes place, the relevant routing +configuration is made available to those proxies. For each metadataFilter in +this list, if its filterMatchCriteria is set to MATCH_ANY, at least one of the +filterLabels must match the corresponding label provided in the metadata. If its +filterMatchCriteria is set to MATCH_ALL, then all of its filterLabels must match +with corresponding labels in the provided metadata. metadataFilters specified +here can be overrides those specified in ForwardingRule that refers to this +UrlMap. metadataFilters only applies to Loadbalancers that have their +loadBalancingScheme set to INTERNAL_SELF_MANAGED.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "filter_labels": { + Type: schema.TypeList, + Required: true, + Description: `The list of label value pairs that must match labels in the provided metadata +based on filterMatchCriteria This list must not be empty and can have at the +most 64 entries.`, + MinItems: 1, + MaxItems: 64, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `Name of metadata label. The name can have a maximum length of 1024 characters +and must be at least 1 character long.`, + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the label must match the specified value. value can have a maximum +length of 1024 characters.`, + }, + }, + }, + }, + "filter_match_criteria": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"MATCH_ALL", "MATCH_ANY"}, false), + Description: `Specifies how individual filterLabel matches within the list of filterLabels +contribute towards the overall metadataFilter match. Supported values are: + - MATCH_ANY: At least one of the filterLabels must have a matching label in the +provided metadata. + - MATCH_ALL: All filterLabels must have matching labels in +the provided metadata. Possible values: ["MATCH_ALL", "MATCH_ANY"]`, + }, + }, + }, + }, + "prefix_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satifying the matchRule condition, the request's path must begin with the +specified prefixMatch. prefixMatch must begin with a /. The value must be +between 1 and 1024 characters. Only one of prefixMatch, fullPathMatch or +regexMatch must be specified.`, + }, + "query_parameter_matches": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies a list of query parameter match criteria, all of which must match +corresponding query parameters in the request.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the query parameter to match. The query parameter must exist in the +request, in the absence of which the request match fails.`, + }, + "exact_match": { + Type: schema.TypeString, + Optional: true, + Description: `The queryParameterMatch matches if the value of the parameter exactly matches +the contents of exactMatch. Only one of presentMatch, exactMatch and regexMatch +must be set.`, + }, + "present_match": { + Type: schema.TypeBool, + Optional: true, + Description: `Specifies that the queryParameterMatch matches if the request contains the query +parameter, irrespective of whether the parameter has a value or not. Only one of +presentMatch, exactMatch and regexMatch must be set.`, + }, + "regex_match": { + Type: schema.TypeString, + Optional: true, + Description: `The queryParameterMatch matches if the value of the parameter matches the +regular expression specified by regexMatch. For the regular expression grammar, +please see en.cppreference.com/w/cpp/regex/ecmascript Only one of presentMatch, +exactMatch and regexMatch must be set.`, + }, + }, + }, + }, + "regex_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satifying the matchRule condition, the path of the request must satisfy the +regular expression specified in regexMatch after removing any query parameters +and anchor supplied with the original URL. For regular expression grammar please +see en.cppreference.com/w/cpp/regex/ecmascript Only one of prefixMatch, +fullPathMatch or regexMatch must be specified.`, + }, + }, + }, + }, + "route_action": { + Type: schema.TypeList, + Optional: true, + Description: `In response to a matching matchRule, the load balancer performs advanced routing +actions like URL rewrites, header transformations, etc. prior to forwarding the +request to the selected backend. If routeAction specifies any +weightedBackendServices, service must not be set. Conversely if service is set, +routeAction cannot contain any weightedBackendServices. Only one of routeAction +or urlRedirect must be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cors_policy": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for allowing client side cross-origin requests. Please see W3C +Recommendation for Cross Origin Resource Sharing`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_credentials": { + Type: schema.TypeBool, + Optional: true, + Description: `In response to a preflight request, setting this to true indicates that the +actual request can include user credentials. This translates to the Access- +Control-Allow-Credentials header. Defaults to false.`, + Default: false, + }, + "allow_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Headers header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_methods": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Methods header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_origin_regexes": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the regualar expression patterns that match allowed origins. For +regular expression grammar please see en.cppreference.com/w/cpp/regex/ecmascript +An origin is allowed if it matches either allow_origins or allow_origin_regex.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_origins": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the list of origins that will be allowed to do CORS requests. An +origin is allowed if it matches either allow_origins or allow_origin_regex.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "disabled": { + Type: schema.TypeBool, + Optional: true, + Description: `If true, specifies the CORS policy is disabled. +which indicates that the CORS policy is in effect. Defaults to false.`, + Default: false, + }, + "expose_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Expose-Headers header.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "max_age": { + Type: schema.TypeInt, + Optional: true, + Description: `Specifies how long the results of a preflight request can be cached. This +translates to the content for the Access-Control-Max-Age header.`, + }, + }, + }, + }, + "fault_injection_policy": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for fault injection introduced into traffic to test the +resiliency of clients to backend service failure. As part of fault injection, +when clients send requests to a backend service, delays can be introduced by +Loadbalancer on a percentage of requests before sending those request to the +backend service. Similarly requests from clients can be aborted by the +Loadbalancer for a percentage of requests. timeout and retry_policy will be +ignored by clients that are configured with a fault_injection_policy.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "abort": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for how client requests are aborted as part of fault +injection.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_status": { + Type: schema.TypeInt, + Optional: true, + Description: `The HTTP status code used to abort the request. The value must be between 200 +and 599 inclusive.`, + }, + "percentage": { + Type: schema.TypeFloat, + Optional: true, + Description: `The percentage of traffic (connections/operations/requests) which will be +aborted as part of fault injection. The value must be between 0.0 and 100.0 +inclusive.`, + }, + }, + }, + }, + "delay": { + Type: schema.TypeList, + Optional: true, + Description: `The specification for how client requests are delayed as part of fault +injection, before being sent to a backend service.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fixed_delay": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the value of the fixed delay interval.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "percentage": { + Type: schema.TypeFloat, + Optional: true, + Description: `The percentage of traffic (connections/operations/requests) on which delay will +be introduced as part of fault injection. The value must be between 0.0 and +100.0 inclusive.`, + }, + }, + }, + }, + }, + }, + }, + "request_mirror_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the policy on how requests intended for the route's backends are +shadowed to a separate mirrored backend service. Loadbalancer does not wait for +responses from the shadow service. Prior to sending traffic to the shadow +service, the host / authority header is suffixed with -shadow.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend_service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The RegionBackendService resource being mirrored to.`, + }, + }, + }, + }, + "retry_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the retry policy associated with this route.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "num_retries": { + Type: schema.TypeInt, + Required: true, + Description: `Specifies the allowed number retries. This number must be > 0.`, + }, + "per_try_timeout": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies a non-zero timeout per retry attempt.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "retry_conditions": { + Type: schema.TypeList, + Optional: true, + Description: `Specfies one or more conditions when this retry rule applies. Valid values are: +- 5xx: Loadbalancer will attempt a retry if the backend service responds with + any 5xx response code, or if the backend service does not respond at all, + example: disconnects, reset, read timeout, connection failure, and refused + streams. +- gateway-error: Similar to 5xx, but only applies to response codes + 502, 503 or 504. +- connect-failure: Loadbalancer will retry on failures + connecting to backend services, for example due to connection timeouts. +- retriable-4xx: Loadbalancer will retry for retriable 4xx response codes. + Currently the only retriable error supported is 409. +- refused-stream: Loadbalancer will retry if the backend service resets the stream with a + REFUSED_STREAM error code. This reset type indicates that it is safe to retry. +- cancelled: Loadbalancer will retry if the gRPC status code in the response + header is set to cancelled +- deadline-exceeded: Loadbalancer will retry if the + gRPC status code in the response header is set to deadline-exceeded +- resource-exhausted: Loadbalancer will retry if the gRPC status code in the response + header is set to resource-exhausted +- unavailable: Loadbalancer will retry if the gRPC status code in + the response header is set to unavailable`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "timeout": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the timeout for the selected route. Timeout is computed from the time +the request is has been fully processed (i.e. end-of-stream) up until the +response has been completely processed. Timeout includes all retries. If not +specified, the default value is 15 seconds.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "seconds": { + Type: schema.TypeString, + Required: true, + Description: `Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 +inclusive.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + Description: `Span of time that's a fraction of a second at nanosecond resolution. Durations +less than one second are represented with a 0 'seconds' field and a positive +'nanos' field. Must be from 0 to 999,999,999 inclusive.`, + }, + }, + }, + }, + "url_rewrite": { + Type: schema.TypeList, + Optional: true, + Description: `The spec to modify the URL of the request, prior to forwarding the request to +the matched service`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected service, the request's host +header is replaced with contents of hostRewrite. The value must be between 1 and +255 characters.`, + }, + "path_prefix_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected backend service, the matching +portion of the request's path is replaced by pathPrefixRewrite. The value must +be between 1 and 1024 characters.`, + }, + }, + }, + }, + "weighted_backend_services": { + Type: schema.TypeList, + Optional: true, + Description: `A list of weighted backend services to send traffic to when a route match +occurs. The weights determine the fraction of traffic that flows to their +corresponding backend service. If all traffic needs to go to a single backend +service, there must be one weightedBackendService with weight set to a non 0 +number. Once a backendService is identified and before forwarding the request to +the backend service, advanced routing actions like Url rewrites and header +transformations are applied depending on additional settings specified in this +HttpRouteAction.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "backend_service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The default RegionBackendService resource. Before +forwarding the request to backendService, the loadbalancer applies any relevant +headerActions specified as part of this backendServiceWeight.`, + }, + "weight": { + Type: schema.TypeInt, + Required: true, + Description: `Specifies the fraction of traffic sent to backendService, computed as weight / +(sum of all weightedBackendService weights in routeAction) . The selection of a +backend service is determined only for new traffic. Once a user's request has +been directed to a backendService, subsequent requests will be sent to the same +backendService as determined by the BackendService's session affinity policy. +The value must be between 0 and 1000`, + }, + "header_action": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies changes to request and response headers that need to take effect for +the selected backendService. headerAction specified here take effect before +headerAction in the enclosing HttpRouteRule, PathMatcher and UrlMap.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add to a matching request prior to forwarding the request to the +backendService.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "request_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the request +prior to forwarding the request to the backendService.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "response_headers_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add the response prior to sending the response back to the client.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Required: true, + Description: `If false, headerValue is appended to any values that already exist for the +header. If true, headerValue is set for the header, discarding any values that +were set for that header.`, + }, + }, + }, + }, + "response_headers_to_remove": { + Type: schema.TypeList, + Optional: true, + Description: `A list of header names for headers that need to be removed from the response +prior to sending the response back to the client.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "service": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The region backend service resource to which traffic is +directed if this rule is matched. If routeAction is additionally specified, +advanced routing actions like URL Rewrites, etc. take effect prior to sending +the request to the backend. However, if service is specified, routeAction cannot +contain any weightedBackendService s. Conversely, if routeAction specifies any +weightedBackendServices, service must not be specified. Only one of urlRedirect, +service or routeAction.weightedBackendService must be set.`, + }, + "url_redirect": { + Type: schema.TypeList, + Optional: true, + Description: `When this rule is matched, the request is redirected to a URL specified by +urlRedirect. If urlRedirect is specified, service or routeAction must not be +set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The host that will be used in the redirect response instead of the one +that was supplied in the request. The value must be between 1 and 255 +characters.`, + }, + "https_redirect": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, the URL scheme in the redirected request is set to https. +If set to false, the URL scheme of the redirected request will remain the +same as that of the request. This must only be set for UrlMaps used in +TargetHttpProxys. Setting this true for TargetHttpsProxy is not +permitted. The default is set to false.`, + Default: false, + }, + "path_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The path that will be used in the redirect response instead of the one +that was supplied in the request. pathRedirect cannot be supplied +together with prefixRedirect. Supply one alone or neither. If neither is +supplied, the path of the original request will be used for the redirect. +The value must be between 1 and 1024 characters.`, + }, + "prefix_redirect": { + Type: schema.TypeString, + Optional: true, + Description: `The prefix that replaces the prefixMatch specified in the +HttpRouteRuleMatch, retaining the remaining portion of the URL before +redirecting the request. prefixRedirect cannot be supplied together with +pathRedirect. Supply one alone or neither. If neither is supplied, the +path of the original request will be used for the redirect. The value +must be between 1 and 1024 characters.`, + }, + "redirect_response_code": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT", ""}, false), + Description: `The HTTP Status code to use for this RedirectAction. Supported values are: +- MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. +- FOUND, which corresponds to 302. +- SEE_OTHER which corresponds to 303. +- TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method +will be retained. +- PERMANENT_REDIRECT, which corresponds to 308. In this case, +the request method will be retained. Possible values: ["FOUND", "MOVED_PERMANENTLY_DEFAULT", "PERMANENT_REDIRECT", "SEE_OTHER", "TEMPORARY_REDIRECT"]`, + }, + "strip_query": { + Type: schema.TypeBool, + Optional: true, + Description: `If set to true, any accompanying query portion of the original URL is +removed prior to redirecting the request. If set to false, the query +portion of the original URL is retained. The default is set to false.`, + Default: false, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The Region in which the url map should reside. +If it is not provided, the provider region is used.`, + }, + "test": { + Type: schema.TypeList, + Optional: true, + Description: `The list of expected URL mappings. Requests to update this UrlMap will +succeed only if all of the test cases pass.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host": { + Type: schema.TypeString, + Required: true, + Description: `Host portion of the URL.`, + }, + "path": { + Type: schema.TypeString, + Required: true, + Description: `Path portion of the URL.`, + }, + "service": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `A reference to expected RegionBackendService resource the given URL should be mapped to.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `Description of this test case.`, + }, + }, + }, + }, + "creation_timestamp": { + Type: schema.TypeString, + Computed: true, + Description: `Creation timestamp in RFC3339 text format.`, + }, + "fingerprint": { + Type: schema.TypeString, + Computed: true, + Description: `Fingerprint of this resource. This field is used internally during +updates of this resource.`, + }, + "map_id": { + Type: schema.TypeInt, + Computed: true, + Description: `The unique identifier for the resource.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "self_link": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func computeRegionUrlMapHostRuleSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hosts": { + Type: schema.TypeSet, + Required: true, + Description: `The list of host patterns to match. They must be valid +hostnames, except * will match any string of ([a-z0-9-.]*). In +that case, * must be the first character and must be followed in +the pattern by either - or ..`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, + "path_matcher": { + Type: schema.TypeString, + Required: true, + Description: `The name of the PathMatcher to use to match the path portion of +the URL if the hostRule matches the URL's host portion.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `An optional description of this HostRule. Provide this property +when you create the resource.`, + }, + }, + } +} + +func resourceComputeRegionUrlMapCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + defaultServiceProp, err := expandComputeRegionUrlMapDefaultService(d.Get("default_service"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_service"); !isEmptyValue(reflect.ValueOf(defaultServiceProp)) && (ok || !reflect.DeepEqual(v, defaultServiceProp)) { + obj["defaultService"] = defaultServiceProp + } + descriptionProp, err := expandComputeRegionUrlMapDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + hostRulesProp, err := expandComputeRegionUrlMapHostRule(d.Get("host_rule"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("host_rule"); !isEmptyValue(reflect.ValueOf(hostRulesProp)) && (ok || !reflect.DeepEqual(v, hostRulesProp)) { + obj["hostRules"] = hostRulesProp + } + fingerprintProp, err := expandComputeRegionUrlMapFingerprint(d.Get("fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("fingerprint"); !isEmptyValue(reflect.ValueOf(fingerprintProp)) && (ok || !reflect.DeepEqual(v, fingerprintProp)) { + obj["fingerprint"] = fingerprintProp + } + nameProp, err := expandComputeRegionUrlMapName(d.Get("name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + pathMatchersProp, err := expandComputeRegionUrlMapPathMatcher(d.Get("path_matcher"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("path_matcher"); !isEmptyValue(reflect.ValueOf(pathMatchersProp)) && (ok || !reflect.DeepEqual(v, pathMatchersProp)) { + obj["pathMatchers"] = pathMatchersProp + } + testsProp, err := expandComputeRegionUrlMapTest(d.Get("test"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("test"); !isEmptyValue(reflect.ValueOf(testsProp)) && (ok || !reflect.DeepEqual(v, testsProp)) { + obj["tests"] = testsProp + } + defaultUrlRedirectProp, err := expandComputeRegionUrlMapDefaultUrlRedirect(d.Get("default_url_redirect"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_url_redirect"); !isEmptyValue(reflect.ValueOf(defaultUrlRedirectProp)) && (ok || !reflect.DeepEqual(v, defaultUrlRedirectProp)) { + obj["defaultUrlRedirect"] = defaultUrlRedirectProp + } + regionProp, err := expandComputeRegionUrlMapRegion(d.Get("region"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(regionProp)) && (ok || !reflect.DeepEqual(v, regionProp)) { + obj["region"] = regionProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/urlMaps") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new RegionUrlMap: %#v", obj) + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating RegionUrlMap: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = computeOperationWaitTime( + config, res, project, "Creating RegionUrlMap", + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create RegionUrlMap: %s", err) + } + + log.Printf("[DEBUG] Finished creating RegionUrlMap %q: %#v", d.Id(), res) + + return resourceComputeRegionUrlMapRead(d, meta) +} + +func resourceComputeRegionUrlMapRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return err + } + + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("ComputeRegionUrlMap %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + + if err := d.Set("creation_timestamp", flattenComputeRegionUrlMapCreationTimestamp(res["creationTimestamp"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("default_service", flattenComputeRegionUrlMapDefaultService(res["defaultService"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("description", flattenComputeRegionUrlMapDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("host_rule", flattenComputeRegionUrlMapHostRule(res["hostRules"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("map_id", flattenComputeRegionUrlMapMapId(res["id"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("fingerprint", flattenComputeRegionUrlMapFingerprint(res["fingerprint"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("name", flattenComputeRegionUrlMapName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("path_matcher", flattenComputeRegionUrlMapPathMatcher(res["pathMatchers"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("test", flattenComputeRegionUrlMapTest(res["tests"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("default_url_redirect", flattenComputeRegionUrlMapDefaultUrlRedirect(res["defaultUrlRedirect"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("region", flattenComputeRegionUrlMapRegion(res["region"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { + return fmt.Errorf("Error reading RegionUrlMap: %s", err) + } + + return nil +} + +func resourceComputeRegionUrlMapUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + defaultServiceProp, err := expandComputeRegionUrlMapDefaultService(d.Get("default_service"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_service"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, defaultServiceProp)) { + obj["defaultService"] = defaultServiceProp + } + descriptionProp, err := expandComputeRegionUrlMapDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + hostRulesProp, err := expandComputeRegionUrlMapHostRule(d.Get("host_rule"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("host_rule"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, hostRulesProp)) { + obj["hostRules"] = hostRulesProp + } + fingerprintProp, err := expandComputeRegionUrlMapFingerprint(d.Get("fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("fingerprint"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, fingerprintProp)) { + obj["fingerprint"] = fingerprintProp + } + nameProp, err := expandComputeRegionUrlMapName(d.Get("name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + pathMatchersProp, err := expandComputeRegionUrlMapPathMatcher(d.Get("path_matcher"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("path_matcher"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, pathMatchersProp)) { + obj["pathMatchers"] = pathMatchersProp + } + testsProp, err := expandComputeRegionUrlMapTest(d.Get("test"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("test"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, testsProp)) { + obj["tests"] = testsProp + } + defaultUrlRedirectProp, err := expandComputeRegionUrlMapDefaultUrlRedirect(d.Get("default_url_redirect"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_url_redirect"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, defaultUrlRedirectProp)) { + obj["defaultUrlRedirect"] = defaultUrlRedirectProp + } + regionProp, err := expandComputeRegionUrlMapRegion(d.Get("region"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, regionProp)) { + obj["region"] = regionProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating RegionUrlMap %q: %#v", d.Id(), obj) + res, err := sendRequestWithTimeout(config, "PUT", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating RegionUrlMap %q: %s", d.Id(), err) + } + + err = computeOperationWaitTime( + config, res, project, "Updating RegionUrlMap", + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceComputeRegionUrlMapRead(d, meta) +} + +func resourceComputeRegionUrlMapDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting RegionUrlMap %q", d.Id()) + + res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "RegionUrlMap") + } + + err = computeOperationWaitTime( + config, res, project, "Deleting RegionUrlMap", + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting RegionUrlMap %q: %#v", d.Id(), res) + return nil +} + +func resourceComputeRegionUrlMapImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/regions/(?P[^/]+)/urlMaps/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenComputeRegionUrlMapCreationTimestamp(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapHostRule(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := schema.NewSet(schema.HashResource(computeRegionUrlMapHostRuleSchema()), []interface{}{}) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed.Add(map[string]interface{}{ + "description": flattenComputeRegionUrlMapHostRuleDescription(original["description"], d, config), + "hosts": flattenComputeRegionUrlMapHostRuleHosts(original["hosts"], d, config), + "path_matcher": flattenComputeRegionUrlMapHostRulePathMatcher(original["pathMatcher"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapHostRuleDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapHostRuleHosts(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeRegionUrlMapHostRulePathMatcher(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapMapId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapFingerprint(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcher(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "default_service": flattenComputeRegionUrlMapPathMatcherDefaultService(original["defaultService"], d, config), + "description": flattenComputeRegionUrlMapPathMatcherDescription(original["description"], d, config), + "name": flattenComputeRegionUrlMapPathMatcherName(original["name"], d, config), + "route_rules": flattenComputeRegionUrlMapPathMatcherRouteRules(original["routeRules"], d, config), + "path_rule": flattenComputeRegionUrlMapPathMatcherPathRule(original["pathRules"], d, config), + "default_url_redirect": flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirect(original["defaultUrlRedirect"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherDefaultService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRules(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "priority": flattenComputeRegionUrlMapPathMatcherRouteRulesPriority(original["priority"], d, config), + "service": flattenComputeRegionUrlMapPathMatcherRouteRulesService(original["service"], d, config), + "header_action": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderAction(original["headerAction"], d, config), + "match_rules": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRules(original["matchRules"], d, config), + "route_action": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteAction(original["routeAction"], d, config), + "url_redirect": flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirect(original["urlRedirect"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesPriority(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderAction(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["request_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAdd(original["requestHeadersToAdd"], d, config) + transformed["request_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToRemove(original["requestHeadersToRemove"], d, config) + transformed["response_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAdd(original["responseHeadersToAdd"], d, config) + transformed["response_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToRemove(original["responseHeadersToRemove"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRules(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "full_path_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesFullPathMatch(original["fullPathMatch"], d, config), + "header_matches": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatches(original["headerMatches"], d, config), + "ignore_case": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesIgnoreCase(original["ignoreCase"], d, config), + "metadata_filters": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFilters(original["metadataFilters"], d, config), + "prefix_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesPrefixMatch(original["prefixMatch"], d, config), + "query_parameter_matches": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatches(original["queryParameterMatches"], d, config), + "regex_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesRegexMatch(original["regexMatch"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesFullPathMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatches(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "exact_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesExactMatch(original["exactMatch"], d, config), + "header_name": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesHeaderName(original["headerName"], d, config), + "invert_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesInvertMatch(original["invertMatch"], d, config), + "prefix_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPrefixMatch(original["prefixMatch"], d, config), + "present_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPresentMatch(original["presentMatch"], d, config), + "range_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatch(original["rangeMatch"], d, config), + "regex_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRegexMatch(original["regexMatch"], d, config), + "suffix_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesSuffixMatch(original["suffixMatch"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesExactMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesInvertMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPrefixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPresentMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["range_end"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeEnd(original["rangeEnd"], d, config) + transformed["range_start"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeStart(original["rangeStart"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeEnd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeStart(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRegexMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesSuffixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesIgnoreCase(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFilters(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "filter_labels": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabels(original["filterLabels"], d, config), + "filter_match_criteria": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterMatchCriteria(original["filterMatchCriteria"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "name": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsName(original["name"], d, config), + "value": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsValue(original["value"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterMatchCriteria(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesPrefixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatches(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "exact_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesExactMatch(original["exactMatch"], d, config), + "name": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesName(original["name"], d, config), + "present_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesPresentMatch(original["presentMatch"], d, config), + "regex_match": flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesRegexMatch(original["regexMatch"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesExactMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesPresentMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesRegexMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesMatchRulesRegexMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteAction(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["cors_policy"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicy(original["corsPolicy"], d, config) + transformed["fault_injection_policy"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicy(original["faultInjectionPolicy"], d, config) + transformed["request_mirror_policy"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicy(original["requestMirrorPolicy"], d, config) + transformed["retry_policy"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicy(original["retryPolicy"], d, config) + transformed["timeout"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeout(original["timeout"], d, config) + transformed["url_rewrite"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewrite(original["urlRewrite"], d, config) + transformed["weighted_backend_services"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServices(original["weightedBackendServices"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["allow_credentials"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowCredentials(original["allowCredentials"], d, config) + transformed["allow_headers"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowHeaders(original["allowHeaders"], d, config) + transformed["allow_methods"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowMethods(original["allowMethods"], d, config) + transformed["allow_origin_regexes"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOriginRegexes(original["allowOriginRegexes"], d, config) + transformed["allow_origins"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOrigins(original["allowOrigins"], d, config) + transformed["disabled"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyDisabled(original["disabled"], d, config) + transformed["expose_headers"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyExposeHeaders(original["exposeHeaders"], d, config) + transformed["max_age"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyMaxAge(original["maxAge"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowCredentials(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowMethods(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOriginRegexes(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOrigins(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyDisabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyExposeHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyMaxAge(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["abort"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbort(original["abort"], d, config) + transformed["delay"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelay(original["delay"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["http_status"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortHttpStatus(original["httpStatus"], d, config) + transformed["percentage"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortPercentage(original["percentage"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortHttpStatus(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortPercentage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelay(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["fixed_delay"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelay(original["fixedDelay"], d, config) + transformed["percentage"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayPercentage(original["percentage"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelay(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelayNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelayNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayPercentage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["backend_service"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicyBackendService(original["backendService"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicyBackendService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["num_retries"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyNumRetries(original["numRetries"], d, config) + transformed["per_try_timeout"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeout(original["perTryTimeout"], d, config) + transformed["retry_conditions"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyRetryConditions(original["retryConditions"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyNumRetries(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeout(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutSeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyRetryConditions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeout(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutSeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_rewrite"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewriteHostRewrite(original["hostRewrite"], d, config) + transformed["path_prefix_rewrite"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewritePathPrefixRewrite(original["pathPrefixRewrite"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewriteHostRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewritePathPrefixRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServices(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "backend_service": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesBackendService(original["backendService"], d, config), + "header_action": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderAction(original["headerAction"], d, config), + "weight": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesWeight(original["weight"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesBackendService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderAction(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["request_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(original["requestHeadersToAdd"], d, config) + transformed["request_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(original["requestHeadersToRemove"], d, config) + transformed["response_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(original["responseHeadersToAdd"], d, config) + transformed["response_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(original["responseHeadersToRemove"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesWeight(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_redirect"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHostRedirect(original["hostRedirect"], d, config) + transformed["https_redirect"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHttpsRedirect(original["httpsRedirect"], d, config) + transformed["path_redirect"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPathRedirect(original["pathRedirect"], d, config) + transformed["prefix_redirect"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPrefixRedirect(original["prefixRedirect"], d, config) + transformed["redirect_response_code"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectRedirectResponseCode(original["redirectResponseCode"], d, config) + transformed["strip_query"] = + flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectStripQuery(original["stripQuery"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHostRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHttpsRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPathRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPrefixRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectRedirectResponseCode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectStripQuery(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRule(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "service": flattenComputeRegionUrlMapPathMatcherPathRuleService(original["service"], d, config), + "paths": flattenComputeRegionUrlMapPathMatcherPathRulePaths(original["paths"], d, config), + "route_action": flattenComputeRegionUrlMapPathMatcherPathRuleRouteAction(original["routeAction"], d, config), + "url_redirect": flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirect(original["urlRedirect"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherPathRuleService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherPathRulePaths(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteAction(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["cors_policy"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicy(original["corsPolicy"], d, config) + transformed["fault_injection_policy"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicy(original["faultInjectionPolicy"], d, config) + transformed["request_mirror_policy"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicy(original["requestMirrorPolicy"], d, config) + transformed["retry_policy"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicy(original["retryPolicy"], d, config) + transformed["timeout"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeout(original["timeout"], d, config) + transformed["url_rewrite"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewrite(original["urlRewrite"], d, config) + transformed["weighted_backend_services"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServices(original["weightedBackendServices"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["allow_credentials"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowCredentials(original["allowCredentials"], d, config) + transformed["allow_headers"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowHeaders(original["allowHeaders"], d, config) + transformed["allow_methods"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowMethods(original["allowMethods"], d, config) + transformed["allow_origin_regexes"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOriginRegexes(original["allowOriginRegexes"], d, config) + transformed["allow_origins"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOrigins(original["allowOrigins"], d, config) + transformed["disabled"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyDisabled(original["disabled"], d, config) + transformed["expose_headers"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyExposeHeaders(original["exposeHeaders"], d, config) + transformed["max_age"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyMaxAge(original["maxAge"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowCredentials(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowMethods(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOriginRegexes(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOrigins(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyDisabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyExposeHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyMaxAge(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["abort"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbort(original["abort"], d, config) + transformed["delay"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelay(original["delay"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["http_status"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortHttpStatus(original["httpStatus"], d, config) + transformed["percentage"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortPercentage(original["percentage"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortHttpStatus(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortPercentage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelay(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["fixed_delay"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelay(original["fixedDelay"], d, config) + transformed["percentage"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayPercentage(original["percentage"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelay(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelayNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelayNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayPercentage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["backend_service"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicyBackendService(original["backendService"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicyBackendService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["num_retries"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyNumRetries(original["numRetries"], d, config) + transformed["per_try_timeout"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeout(original["perTryTimeout"], d, config) + transformed["retry_conditions"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyRetryConditions(original["retryConditions"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyNumRetries(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeout(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutSeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyRetryConditions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeout(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["nanos"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutNanos(original["nanos"], d, config) + transformed["seconds"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutSeconds(original["seconds"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutNanos(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_rewrite"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewriteHostRewrite(original["hostRewrite"], d, config) + transformed["path_prefix_rewrite"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewritePathPrefixRewrite(original["pathPrefixRewrite"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewriteHostRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewritePathPrefixRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServices(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "backend_service": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesBackendService(original["backendService"], d, config), + "header_action": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderAction(original["headerAction"], d, config), + "weight": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesWeight(original["weight"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesBackendService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderAction(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["request_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(original["requestHeadersToAdd"], d, config) + transformed["request_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(original["requestHeadersToRemove"], d, config) + transformed["response_headers_to_add"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(original["responseHeadersToAdd"], d, config) + transformed["response_headers_to_remove"] = + flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(original["responseHeadersToRemove"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "header_name": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(original["headerName"], d, config), + "header_value": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesWeight(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_redirect"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHostRedirect(original["hostRedirect"], d, config) + transformed["https_redirect"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHttpsRedirect(original["httpsRedirect"], d, config) + transformed["path_redirect"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPathRedirect(original["pathRedirect"], d, config) + transformed["prefix_redirect"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPrefixRedirect(original["prefixRedirect"], d, config) + transformed["redirect_response_code"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectRedirectResponseCode(original["redirectResponseCode"], d, config) + transformed["strip_query"] = + flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectStripQuery(original["stripQuery"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHostRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHttpsRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPathRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPrefixRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectRedirectResponseCode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherPathRuleUrlRedirectStripQuery(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_redirect"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectHostRedirect(original["hostRedirect"], d, config) + transformed["https_redirect"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectHttpsRedirect(original["httpsRedirect"], d, config) + transformed["path_redirect"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectPathRedirect(original["pathRedirect"], d, config) + transformed["prefix_redirect"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectPrefixRedirect(original["prefixRedirect"], d, config) + transformed["redirect_response_code"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectRedirectResponseCode(original["redirectResponseCode"], d, config) + transformed["strip_query"] = + flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectStripQuery(original["stripQuery"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectHostRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectHttpsRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectPathRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectPrefixRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectRedirectResponseCode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapPathMatcherDefaultUrlRedirectStripQuery(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapTest(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "description": flattenComputeRegionUrlMapTestDescription(original["description"], d, config), + "host": flattenComputeRegionUrlMapTestHost(original["host"], d, config), + "path": flattenComputeRegionUrlMapTestPath(original["path"], d, config), + "service": flattenComputeRegionUrlMapTestService(original["service"], d, config), + }) + } + return transformed +} +func flattenComputeRegionUrlMapTestDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapTestHost(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapTestPath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapTestService(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRegionUrlMapDefaultUrlRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["host_redirect"] = + flattenComputeRegionUrlMapDefaultUrlRedirectHostRedirect(original["hostRedirect"], d, config) + transformed["https_redirect"] = + flattenComputeRegionUrlMapDefaultUrlRedirectHttpsRedirect(original["httpsRedirect"], d, config) + transformed["path_redirect"] = + flattenComputeRegionUrlMapDefaultUrlRedirectPathRedirect(original["pathRedirect"], d, config) + transformed["prefix_redirect"] = + flattenComputeRegionUrlMapDefaultUrlRedirectPrefixRedirect(original["prefixRedirect"], d, config) + transformed["redirect_response_code"] = + flattenComputeRegionUrlMapDefaultUrlRedirectRedirectResponseCode(original["redirectResponseCode"], d, config) + transformed["strip_query"] = + flattenComputeRegionUrlMapDefaultUrlRedirectStripQuery(original["stripQuery"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionUrlMapDefaultUrlRedirectHostRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultUrlRedirectHttpsRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultUrlRedirectPathRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultUrlRedirectPrefixRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultUrlRedirectRedirectResponseCode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapDefaultUrlRedirectStripQuery(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionUrlMapRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return NameFromSelfLinkStateFunc(v) +} + +func expandComputeRegionUrlMapDefaultService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for default_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapHostRule(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDescription, err := expandComputeRegionUrlMapHostRuleDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedHosts, err := expandComputeRegionUrlMapHostRuleHosts(original["hosts"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHosts); val.IsValid() && !isEmptyValue(val) { + transformed["hosts"] = transformedHosts + } + + transformedPathMatcher, err := expandComputeRegionUrlMapHostRulePathMatcher(original["path_matcher"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathMatcher); val.IsValid() && !isEmptyValue(val) { + transformed["pathMatcher"] = transformedPathMatcher + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapHostRuleDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapHostRuleHosts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + +func expandComputeRegionUrlMapHostRulePathMatcher(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcher(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDefaultService, err := expandComputeRegionUrlMapPathMatcherDefaultService(original["default_service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDefaultService); val.IsValid() && !isEmptyValue(val) { + transformed["defaultService"] = transformedDefaultService + } + + transformedDescription, err := expandComputeRegionUrlMapPathMatcherDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedName, err := expandComputeRegionUrlMapPathMatcherName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedRouteRules, err := expandComputeRegionUrlMapPathMatcherRouteRules(original["route_rules"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRouteRules); val.IsValid() && !isEmptyValue(val) { + transformed["routeRules"] = transformedRouteRules + } + + transformedPathRule, err := expandComputeRegionUrlMapPathMatcherPathRule(original["path_rule"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathRule); val.IsValid() && !isEmptyValue(val) { + transformed["pathRules"] = transformedPathRule + } + + transformedDefaultUrlRedirect, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirect(original["default_url_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDefaultUrlRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["defaultUrlRedirect"] = transformedDefaultUrlRedirect + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for default_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRules(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPriority, err := expandComputeRegionUrlMapPathMatcherRouteRulesPriority(original["priority"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPriority); val.IsValid() && !isEmptyValue(val) { + transformed["priority"] = transformedPriority + } + + transformedService, err := expandComputeRegionUrlMapPathMatcherRouteRulesService(original["service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedService); val.IsValid() && !isEmptyValue(val) { + transformed["service"] = transformedService + } + + transformedHeaderAction, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderAction(original["header_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderAction); val.IsValid() && !isEmptyValue(val) { + transformed["headerAction"] = transformedHeaderAction + } + + transformedMatchRules, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRules(original["match_rules"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMatchRules); val.IsValid() && !isEmptyValue(val) { + transformed["matchRules"] = transformedMatchRules + } + + transformedRouteAction, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteAction(original["route_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRouteAction); val.IsValid() && !isEmptyValue(val) { + transformed["routeAction"] = transformedRouteAction + } + + transformedUrlRedirect, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirect(original["url_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUrlRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["urlRedirect"] = transformedUrlRedirect + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesPriority(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderAction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedRequestHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAdd(original["request_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToAdd"] = transformedRequestHeadersToAdd + } + + transformedRequestHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToRemove(original["request_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToRemove"] = transformedRequestHeadersToRemove + } + + transformedResponseHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAdd(original["response_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToAdd"] = transformedResponseHeadersToAdd + } + + transformedResponseHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToRemove(original["response_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToRemove"] = transformedResponseHeadersToRemove + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionRequestHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesHeaderActionResponseHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRules(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedFullPathMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesFullPathMatch(original["full_path_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFullPathMatch); val.IsValid() && !isEmptyValue(val) { + transformed["fullPathMatch"] = transformedFullPathMatch + } + + transformedHeaderMatches, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatches(original["header_matches"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderMatches); val.IsValid() && !isEmptyValue(val) { + transformed["headerMatches"] = transformedHeaderMatches + } + + transformedIgnoreCase, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesIgnoreCase(original["ignore_case"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIgnoreCase); val.IsValid() && !isEmptyValue(val) { + transformed["ignoreCase"] = transformedIgnoreCase + } + + transformedMetadataFilters, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFilters(original["metadata_filters"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMetadataFilters); val.IsValid() && !isEmptyValue(val) { + transformed["metadataFilters"] = transformedMetadataFilters + } + + transformedPrefixMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesPrefixMatch(original["prefix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["prefixMatch"] = transformedPrefixMatch + } + + transformedQueryParameterMatches, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatches(original["query_parameter_matches"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedQueryParameterMatches); val.IsValid() && !isEmptyValue(val) { + transformed["queryParameterMatches"] = transformedQueryParameterMatches + } + + transformedRegexMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesRegexMatch(original["regex_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRegexMatch); val.IsValid() && !isEmptyValue(val) { + transformed["regexMatch"] = transformedRegexMatch + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesFullPathMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatches(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedExactMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesExactMatch(original["exact_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExactMatch); val.IsValid() && !isEmptyValue(val) { + transformed["exactMatch"] = transformedExactMatch + } + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedInvertMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesInvertMatch(original["invert_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInvertMatch); val.IsValid() && !isEmptyValue(val) { + transformed["invertMatch"] = transformedInvertMatch + } + + transformedPrefixMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPrefixMatch(original["prefix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["prefixMatch"] = transformedPrefixMatch + } + + transformedPresentMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPresentMatch(original["present_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPresentMatch); val.IsValid() && !isEmptyValue(val) { + transformed["presentMatch"] = transformedPresentMatch + } + + transformedRangeMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatch(original["range_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRangeMatch); val.IsValid() && !isEmptyValue(val) { + transformed["rangeMatch"] = transformedRangeMatch + } + + transformedRegexMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRegexMatch(original["regex_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRegexMatch); val.IsValid() && !isEmptyValue(val) { + transformed["regexMatch"] = transformedRegexMatch + } + + transformedSuffixMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesSuffixMatch(original["suffix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuffixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["suffixMatch"] = transformedSuffixMatch + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesExactMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesInvertMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPrefixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesPresentMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedRangeEnd, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeEnd(original["range_end"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRangeEnd); val.IsValid() && !isEmptyValue(val) { + transformed["rangeEnd"] = transformedRangeEnd + } + + transformedRangeStart, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeStart(original["range_start"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRangeStart); val.IsValid() && !isEmptyValue(val) { + transformed["rangeStart"] = transformedRangeStart + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeEnd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRangeMatchRangeStart(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesRegexMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesHeaderMatchesSuffixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesIgnoreCase(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFilters(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedFilterLabels, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabels(original["filter_labels"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFilterLabels); val.IsValid() && !isEmptyValue(val) { + transformed["filterLabels"] = transformedFilterLabels + } + + transformedFilterMatchCriteria, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterMatchCriteria(original["filter_match_criteria"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFilterMatchCriteria); val.IsValid() && !isEmptyValue(val) { + transformed["filterMatchCriteria"] = transformedFilterMatchCriteria + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabels(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedValue, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsValue(original["value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedValue); val.IsValid() && !isEmptyValue(val) { + transformed["value"] = transformedValue + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterLabelsValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesMetadataFiltersFilterMatchCriteria(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesPrefixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatches(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedExactMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesExactMatch(original["exact_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExactMatch); val.IsValid() && !isEmptyValue(val) { + transformed["exactMatch"] = transformedExactMatch + } + + transformedName, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPresentMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesPresentMatch(original["present_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPresentMatch); val.IsValid() && !isEmptyValue(val) { + transformed["presentMatch"] = transformedPresentMatch + } + + transformedRegexMatch, err := expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesRegexMatch(original["regex_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRegexMatch); val.IsValid() && !isEmptyValue(val) { + transformed["regexMatch"] = transformedRegexMatch + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesExactMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesPresentMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesQueryParameterMatchesRegexMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesMatchRulesRegexMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteAction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedCorsPolicy, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicy(original["cors_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCorsPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["corsPolicy"] = transformedCorsPolicy + } + + transformedFaultInjectionPolicy, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicy(original["fault_injection_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFaultInjectionPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["faultInjectionPolicy"] = transformedFaultInjectionPolicy + } + + transformedRequestMirrorPolicy, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicy(original["request_mirror_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestMirrorPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["requestMirrorPolicy"] = transformedRequestMirrorPolicy + } + + transformedRetryPolicy, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicy(original["retry_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRetryPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["retryPolicy"] = transformedRetryPolicy + } + + transformedTimeout, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeout(original["timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["timeout"] = transformedTimeout + } + + transformedUrlRewrite, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewrite(original["url_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUrlRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["urlRewrite"] = transformedUrlRewrite + } + + transformedWeightedBackendServices, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServices(original["weighted_backend_services"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWeightedBackendServices); val.IsValid() && !isEmptyValue(val) { + transformed["weightedBackendServices"] = transformedWeightedBackendServices + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAllowCredentials, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowCredentials(original["allow_credentials"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowCredentials); val.IsValid() && !isEmptyValue(val) { + transformed["allowCredentials"] = transformedAllowCredentials + } + + transformedAllowHeaders, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowHeaders(original["allow_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["allowHeaders"] = transformedAllowHeaders + } + + transformedAllowMethods, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowMethods(original["allow_methods"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowMethods); val.IsValid() && !isEmptyValue(val) { + transformed["allowMethods"] = transformedAllowMethods + } + + transformedAllowOriginRegexes, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOriginRegexes(original["allow_origin_regexes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowOriginRegexes); val.IsValid() && !isEmptyValue(val) { + transformed["allowOriginRegexes"] = transformedAllowOriginRegexes + } + + transformedAllowOrigins, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOrigins(original["allow_origins"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowOrigins); val.IsValid() && !isEmptyValue(val) { + transformed["allowOrigins"] = transformedAllowOrigins + } + + transformedDisabled, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + transformedExposeHeaders, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyExposeHeaders(original["expose_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExposeHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["exposeHeaders"] = transformedExposeHeaders + } + + transformedMaxAge, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyMaxAge(original["max_age"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxAge); val.IsValid() && !isEmptyValue(val) { + transformed["maxAge"] = transformedMaxAge + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowCredentials(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowMethods(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOriginRegexes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyAllowOrigins(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyExposeHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionCorsPolicyMaxAge(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAbort, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbort(original["abort"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAbort); val.IsValid() && !isEmptyValue(val) { + transformed["abort"] = transformedAbort + } + + transformedDelay, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelay(original["delay"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDelay); val.IsValid() && !isEmptyValue(val) { + transformed["delay"] = transformedDelay + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHttpStatus, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortHttpStatus(original["http_status"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpStatus); val.IsValid() && !isEmptyValue(val) { + transformed["httpStatus"] = transformedHttpStatus + } + + transformedPercentage, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortPercentage(original["percentage"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPercentage); val.IsValid() && !isEmptyValue(val) { + transformed["percentage"] = transformedPercentage + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortHttpStatus(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyAbortPercentage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelay(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedFixedDelay, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelay(original["fixed_delay"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFixedDelay); val.IsValid() && !isEmptyValue(val) { + transformed["fixedDelay"] = transformedFixedDelay + } + + transformedPercentage, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayPercentage(original["percentage"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPercentage); val.IsValid() && !isEmptyValue(val) { + transformed["percentage"] = transformedPercentage + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelay(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelayNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelayNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionFaultInjectionPolicyDelayPercentage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBackendService, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicyBackendService(original["backend_service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBackendService); val.IsValid() && !isEmptyValue(val) { + transformed["backendService"] = transformedBackendService + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRequestMirrorPolicyBackendService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for backend_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNumRetries, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyNumRetries(original["num_retries"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNumRetries); val.IsValid() && !isEmptyValue(val) { + transformed["numRetries"] = transformedNumRetries + } + + transformedPerTryTimeout, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeout(original["per_try_timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPerTryTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["perTryTimeout"] = transformedPerTryTimeout + } + + transformedRetryConditions, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyRetryConditions(original["retry_conditions"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRetryConditions); val.IsValid() && !isEmptyValue(val) { + transformed["retryConditions"] = transformedRetryConditions + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyNumRetries(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyPerTryTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionRetryPolicyRetryConditions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRewrite, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewriteHostRewrite(original["host_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["hostRewrite"] = transformedHostRewrite + } + + transformedPathPrefixRewrite, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewritePathPrefixRewrite(original["path_prefix_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathPrefixRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["pathPrefixRewrite"] = transformedPathPrefixRewrite + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewriteHostRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionUrlRewritePathPrefixRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServices(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBackendService, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesBackendService(original["backend_service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBackendService); val.IsValid() && !isEmptyValue(val) { + transformed["backendService"] = transformedBackendService + } + + transformedHeaderAction, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderAction(original["header_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderAction); val.IsValid() && !isEmptyValue(val) { + transformed["headerAction"] = transformedHeaderAction + } + + transformedWeight, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesWeight(original["weight"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWeight); val.IsValid() && !isEmptyValue(val) { + transformed["weight"] = transformedWeight + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesBackendService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for backend_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderAction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedRequestHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(original["request_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToAdd"] = transformedRequestHeadersToAdd + } + + transformedRequestHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(original["request_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToRemove"] = transformedRequestHeadersToRemove + } + + transformedResponseHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(original["response_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToAdd"] = transformedResponseHeadersToAdd + } + + transformedResponseHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(original["response_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToRemove"] = transformedResponseHeadersToRemove + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesRouteActionWeightedBackendServicesWeight(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRedirect, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHostRedirect(original["host_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["hostRedirect"] = transformedHostRedirect + } + + transformedHttpsRedirect, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHttpsRedirect(original["https_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpsRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["httpsRedirect"] = transformedHttpsRedirect + } + + transformedPathRedirect, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPathRedirect(original["path_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["pathRedirect"] = transformedPathRedirect + } + + transformedPrefixRedirect, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPrefixRedirect(original["prefix_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["prefixRedirect"] = transformedPrefixRedirect + } + + transformedRedirectResponseCode, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectRedirectResponseCode(original["redirect_response_code"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRedirectResponseCode); val.IsValid() && !isEmptyValue(val) { + transformed["redirectResponseCode"] = transformedRedirectResponseCode + } + + transformedStripQuery, err := expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectStripQuery(original["strip_query"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStripQuery); val.IsValid() && !isEmptyValue(val) { + transformed["stripQuery"] = transformedStripQuery + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHostRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectHttpsRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPathRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectPrefixRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectRedirectResponseCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherRouteRulesUrlRedirectStripQuery(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRule(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedService, err := expandComputeRegionUrlMapPathMatcherPathRuleService(original["service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedService); val.IsValid() && !isEmptyValue(val) { + transformed["service"] = transformedService + } + + transformedPaths, err := expandComputeRegionUrlMapPathMatcherPathRulePaths(original["paths"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPaths); val.IsValid() && !isEmptyValue(val) { + transformed["paths"] = transformedPaths + } + + transformedRouteAction, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteAction(original["route_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRouteAction); val.IsValid() && !isEmptyValue(val) { + transformed["routeAction"] = transformedRouteAction + } + + transformedUrlRedirect, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirect(original["url_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUrlRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["urlRedirect"] = transformedUrlRedirect + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherPathRulePaths(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteAction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedCorsPolicy, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicy(original["cors_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCorsPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["corsPolicy"] = transformedCorsPolicy + } + + transformedFaultInjectionPolicy, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicy(original["fault_injection_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFaultInjectionPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["faultInjectionPolicy"] = transformedFaultInjectionPolicy + } + + transformedRequestMirrorPolicy, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicy(original["request_mirror_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestMirrorPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["requestMirrorPolicy"] = transformedRequestMirrorPolicy + } + + transformedRetryPolicy, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicy(original["retry_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRetryPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["retryPolicy"] = transformedRetryPolicy + } + + transformedTimeout, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeout(original["timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["timeout"] = transformedTimeout + } + + transformedUrlRewrite, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewrite(original["url_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUrlRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["urlRewrite"] = transformedUrlRewrite + } + + transformedWeightedBackendServices, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServices(original["weighted_backend_services"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWeightedBackendServices); val.IsValid() && !isEmptyValue(val) { + transformed["weightedBackendServices"] = transformedWeightedBackendServices + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAllowCredentials, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowCredentials(original["allow_credentials"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowCredentials); val.IsValid() && !isEmptyValue(val) { + transformed["allowCredentials"] = transformedAllowCredentials + } + + transformedAllowHeaders, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowHeaders(original["allow_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["allowHeaders"] = transformedAllowHeaders + } + + transformedAllowMethods, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowMethods(original["allow_methods"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowMethods); val.IsValid() && !isEmptyValue(val) { + transformed["allowMethods"] = transformedAllowMethods + } + + transformedAllowOriginRegexes, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOriginRegexes(original["allow_origin_regexes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowOriginRegexes); val.IsValid() && !isEmptyValue(val) { + transformed["allowOriginRegexes"] = transformedAllowOriginRegexes + } + + transformedAllowOrigins, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOrigins(original["allow_origins"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowOrigins); val.IsValid() && !isEmptyValue(val) { + transformed["allowOrigins"] = transformedAllowOrigins + } + + transformedDisabled, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + transformedExposeHeaders, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyExposeHeaders(original["expose_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExposeHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["exposeHeaders"] = transformedExposeHeaders + } + + transformedMaxAge, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyMaxAge(original["max_age"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxAge); val.IsValid() && !isEmptyValue(val) { + transformed["maxAge"] = transformedMaxAge + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowCredentials(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowMethods(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOriginRegexes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyAllowOrigins(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyExposeHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionCorsPolicyMaxAge(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAbort, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbort(original["abort"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAbort); val.IsValid() && !isEmptyValue(val) { + transformed["abort"] = transformedAbort + } + + transformedDelay, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelay(original["delay"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDelay); val.IsValid() && !isEmptyValue(val) { + transformed["delay"] = transformedDelay + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHttpStatus, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortHttpStatus(original["http_status"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpStatus); val.IsValid() && !isEmptyValue(val) { + transformed["httpStatus"] = transformedHttpStatus + } + + transformedPercentage, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortPercentage(original["percentage"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPercentage); val.IsValid() && !isEmptyValue(val) { + transformed["percentage"] = transformedPercentage + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortHttpStatus(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyAbortPercentage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelay(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedFixedDelay, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelay(original["fixed_delay"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFixedDelay); val.IsValid() && !isEmptyValue(val) { + transformed["fixedDelay"] = transformedFixedDelay + } + + transformedPercentage, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayPercentage(original["percentage"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPercentage); val.IsValid() && !isEmptyValue(val) { + transformed["percentage"] = transformedPercentage + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelay(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelayNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelayNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayFixedDelaySeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionFaultInjectionPolicyDelayPercentage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBackendService, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicyBackendService(original["backend_service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBackendService); val.IsValid() && !isEmptyValue(val) { + transformed["backendService"] = transformedBackendService + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRequestMirrorPolicyBackendService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for backend_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNumRetries, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyNumRetries(original["num_retries"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNumRetries); val.IsValid() && !isEmptyValue(val) { + transformed["numRetries"] = transformedNumRetries + } + + transformedPerTryTimeout, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeout(original["per_try_timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPerTryTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["perTryTimeout"] = transformedPerTryTimeout + } + + transformedRetryConditions, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyRetryConditions(original["retry_conditions"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRetryConditions); val.IsValid() && !isEmptyValue(val) { + transformed["retryConditions"] = transformedRetryConditions + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyNumRetries(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyPerTryTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionRetryPolicyRetryConditions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedNanos, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !isEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + transformedSeconds, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutNanos(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRewrite, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewriteHostRewrite(original["host_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["hostRewrite"] = transformedHostRewrite + } + + transformedPathPrefixRewrite, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewritePathPrefixRewrite(original["path_prefix_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathPrefixRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["pathPrefixRewrite"] = transformedPathPrefixRewrite + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewriteHostRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionUrlRewritePathPrefixRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServices(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedBackendService, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesBackendService(original["backend_service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBackendService); val.IsValid() && !isEmptyValue(val) { + transformed["backendService"] = transformedBackendService + } + + transformedHeaderAction, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderAction(original["header_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderAction); val.IsValid() && !isEmptyValue(val) { + transformed["headerAction"] = transformedHeaderAction + } + + transformedWeight, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesWeight(original["weight"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWeight); val.IsValid() && !isEmptyValue(val) { + transformed["weight"] = transformedWeight + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesBackendService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for backend_service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderAction(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedRequestHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(original["request_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToAdd"] = transformedRequestHeadersToAdd + } + + transformedRequestHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(original["request_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToRemove"] = transformedRequestHeadersToRemove + } + + transformedResponseHeadersToAdd, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(original["response_headers_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToAdd"] = transformedResponseHeadersToAdd + } + + transformedResponseHeadersToRemove, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(original["response_headers_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeadersToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToRemove"] = transformedResponseHeadersToRemove + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionRequestHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAdd(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHeaderName, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedHeaderValue, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(original["header_value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderValue); val.IsValid() && !isEmptyValue(val) { + transformed["headerValue"] = transformedHeaderValue + } + + transformedReplace, err := expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(original["replace"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedReplace); val.IsValid() && !isEmptyValue(val) { + transformed["replace"] = transformedReplace + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesHeaderActionResponseHeadersToRemove(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleRouteActionWeightedBackendServicesWeight(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRedirect, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHostRedirect(original["host_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["hostRedirect"] = transformedHostRedirect + } + + transformedHttpsRedirect, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHttpsRedirect(original["https_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpsRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["httpsRedirect"] = transformedHttpsRedirect + } + + transformedPathRedirect, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPathRedirect(original["path_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["pathRedirect"] = transformedPathRedirect + } + + transformedPrefixRedirect, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPrefixRedirect(original["prefix_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["prefixRedirect"] = transformedPrefixRedirect + } + + transformedRedirectResponseCode, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectRedirectResponseCode(original["redirect_response_code"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRedirectResponseCode); val.IsValid() && !isEmptyValue(val) { + transformed["redirectResponseCode"] = transformedRedirectResponseCode + } + + transformedStripQuery, err := expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectStripQuery(original["strip_query"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStripQuery); val.IsValid() && !isEmptyValue(val) { + transformed["stripQuery"] = transformedStripQuery + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHostRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectHttpsRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPathRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectPrefixRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectRedirectResponseCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherPathRuleUrlRedirectStripQuery(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRedirect, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectHostRedirect(original["host_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["hostRedirect"] = transformedHostRedirect + } + + transformedHttpsRedirect, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectHttpsRedirect(original["https_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpsRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["httpsRedirect"] = transformedHttpsRedirect + } + + transformedPathRedirect, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectPathRedirect(original["path_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["pathRedirect"] = transformedPathRedirect + } + + transformedPrefixRedirect, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectPrefixRedirect(original["prefix_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["prefixRedirect"] = transformedPrefixRedirect + } + + transformedRedirectResponseCode, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectRedirectResponseCode(original["redirect_response_code"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRedirectResponseCode); val.IsValid() && !isEmptyValue(val) { + transformed["redirectResponseCode"] = transformedRedirectResponseCode + } + + transformedStripQuery, err := expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectStripQuery(original["strip_query"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStripQuery); val.IsValid() && !isEmptyValue(val) { + transformed["stripQuery"] = transformedStripQuery + } + + return transformed, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectHostRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectHttpsRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectPathRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectPrefixRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectRedirectResponseCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapPathMatcherDefaultUrlRedirectStripQuery(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapTest(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDescription, err := expandComputeRegionUrlMapTestDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedHost, err := expandComputeRegionUrlMapTestHost(original["host"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHost); val.IsValid() && !isEmptyValue(val) { + transformed["host"] = transformedHost + } + + transformedPath, err := expandComputeRegionUrlMapTestPath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + transformedService, err := expandComputeRegionUrlMapTestService(original["service"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedService); val.IsValid() && !isEmptyValue(val) { + transformed["service"] = transformedService + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRegionUrlMapTestDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapTestHost(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapTestPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapTestService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("backendServices", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for service: %s", err) + } + return f.RelativeLink(), nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedHostRedirect, err := expandComputeRegionUrlMapDefaultUrlRedirectHostRedirect(original["host_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["hostRedirect"] = transformedHostRedirect + } + + transformedHttpsRedirect, err := expandComputeRegionUrlMapDefaultUrlRedirectHttpsRedirect(original["https_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpsRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["httpsRedirect"] = transformedHttpsRedirect + } + + transformedPathRedirect, err := expandComputeRegionUrlMapDefaultUrlRedirectPathRedirect(original["path_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["pathRedirect"] = transformedPathRedirect + } + + transformedPrefixRedirect, err := expandComputeRegionUrlMapDefaultUrlRedirectPrefixRedirect(original["prefix_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["prefixRedirect"] = transformedPrefixRedirect + } + + transformedRedirectResponseCode, err := expandComputeRegionUrlMapDefaultUrlRedirectRedirectResponseCode(original["redirect_response_code"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRedirectResponseCode); val.IsValid() && !isEmptyValue(val) { + transformed["redirectResponseCode"] = transformedRedirectResponseCode + } + + transformedStripQuery, err := expandComputeRegionUrlMapDefaultUrlRedirectStripQuery(original["strip_query"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStripQuery); val.IsValid() && !isEmptyValue(val) { + transformed["stripQuery"] = transformedStripQuery + } + + return transformed, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectHostRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectHttpsRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectPathRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectPrefixRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectRedirectResponseCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapDefaultUrlRedirectStripQuery(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionUrlMapRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for region: %s", err) + } + return f.RelativeLink(), nil +} diff --git a/google/resource_compute_region_url_map_generated_test.go b/google/resource_compute_region_url_map_generated_test.go new file mode 100644 index 00000000000..328c8180af9 --- /dev/null +++ b/google/resource_compute_region_url_map_generated_test.go @@ -0,0 +1,571 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccComputeRegionUrlMap_regionUrlMapBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_regionUrlMapBasicExample(context), + }, + { + ResourceName: "google_compute_region_url_map.regionurlmap", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_regionUrlMapBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_url_map" "regionurlmap" { + region = "us-central1" + + name = "regionurlmap%{random_suffix}" + description = "a description" + + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + service = google_compute_region_backend_service.home.self_link + } + + path_rule { + paths = ["/login"] + service = google_compute_region_backend_service.login.self_link + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "login" { + region = "us-central1" + + name = "login%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_backend_service" "home" { + region = "us-central1" + + name = "home%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + + name = "tf-test-health-check%{random_suffix}" + check_interval_sec = 1 + timeout_sec = 1 + http_health_check { + port = 80 + request_path = "/" + } +} +`, context) +} + +func TestAccComputeRegionUrlMap_regionUrlMapL7IlbPathExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_regionUrlMapL7IlbPathExample(context), + }, + { + ResourceName: "google_compute_region_url_map.regionurlmap", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_regionUrlMapL7IlbPathExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap%{random_suffix}" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + route_action { + cors_policy { + allow_credentials = true + allow_headers = ["Allowed content"] + allow_methods = ["GET"] + allow_origins = ["Allowed origin"] + expose_headers = ["Exposed header"] + max_age = 30 + disabled = false + } + fault_injection_policy { + abort { + http_status = 234 + percentage = 5.6 + } + delay { + fixed_delay { + seconds = 0 + nanos = 50000 + } + percentage = 7.8 + } + } + request_mirror_policy { + backend_service = google_compute_region_backend_service.home.self_link + } + retry_policy { + num_retries = 4 + per_try_timeout { + seconds = 30 + } + retry_conditions = ["5xx", "deadline-exceeded"] + } + timeout { + seconds = 20 + nanos = 750000000 + } + url_rewrite { + host_rewrite = "A replacement header" + path_prefix_rewrite = "A replacement path" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 400 + header_action { + request_headers_to_remove = ["RemoveMe"] + request_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = true + } + response_headers_to_remove = ["RemoveMe"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "tf-test-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionUrlMap_regionUrlMapL7IlbPathPartialExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_regionUrlMapL7IlbPathPartialExample(context), + }, + { + ResourceName: "google_compute_region_url_map.regionurlmap", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_regionUrlMapL7IlbPathPartialExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap%{random_suffix}" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + route_action { + retry_policy { + num_retries = 4 + per_try_timeout { + seconds = 30 + } + retry_conditions = ["5xx", "deadline-exceeded"] + } + timeout { + seconds = 20 + nanos = 750000000 + } + url_rewrite { + host_rewrite = "A replacement header" + path_prefix_rewrite = "A replacement path" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 400 + header_action { + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "tf-test-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionUrlMap_regionUrlMapL7IlbRouteExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_regionUrlMapL7IlbRouteExample(context), + }, + { + ResourceName: "google_compute_region_url_map.regionurlmap", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_regionUrlMapL7IlbRouteExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap%{random_suffix}" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + route_rules { + priority = 1 + header_action { + request_headers_to_remove = ["RemoveMe2"] + request_headers_to_add { + header_name = "AddSomethingElse" + header_value = "MyOtherValue" + replace = true + } + response_headers_to_remove = ["RemoveMe3"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + match_rules { + full_path_match = "a full path" + header_matches { + header_name = "someheader" + exact_match = "match this exactly" + invert_match = true + } + ignore_case = true + metadata_filters { + filter_match_criteria = "MATCH_ANY" + filter_labels { + name = "PLANET" + value = "MARS" + } + } + query_parameter_matches { + name = "a query parameter" + present_match = true + } + } + url_redirect { + host_redirect = "A host" + https_redirect = false + path_redirect = "some/path" + redirect_response_code = "TEMPORARY_REDIRECT" + strip_query = true + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "tf-test-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func TestAccComputeRegionUrlMap_regionUrlMapL7IlbRoutePartialExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_regionUrlMapL7IlbRoutePartialExample(context), + }, + { + ResourceName: "google_compute_region_url_map.regionurlmap", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_regionUrlMapL7IlbRoutePartialExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap%{random_suffix}" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + route_rules { + priority = 1 + service = google_compute_region_backend_service.home.self_link + header_action { + request_headers_to_remove = ["RemoveMe2"] + } + match_rules { + full_path_match = "a full path" + header_matches { + header_name = "someheader" + exact_match = "match this exactly" + invert_match = true + } + query_parameter_matches { + name = "a query parameter" + present_match = true + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home%{random_suffix}" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "tf-test-health-check%{random_suffix}" + http_health_check { + port = 80 + } +} +`, context) +} + +func testAccCheckComputeRegionUrlMapDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_url_map" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/urlMaps/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", "", url, nil) + if err == nil { + return fmt.Errorf("ComputeRegionUrlMap still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_compute_region_url_map_sweeper_test.go b/google/resource_compute_region_url_map_sweeper_test.go new file mode 100644 index 00000000000..99071828f5b --- /dev/null +++ b/google/resource_compute_region_url_map_sweeper_test.go @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func init() { + resource.AddTestSweepers("ComputeRegionUrlMap", &resource.Sweeper{ + Name: "ComputeRegionUrlMap", + F: testSweepComputeRegionUrlMap, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepComputeRegionUrlMap(region string) error { + resourceName := "ComputeRegionUrlMap" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/urlMaps", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["items"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/urlMaps/{{name}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/resource_compute_region_url_map_test.go b/google/resource_compute_region_url_map_test.go index 71664db3c87..a34ecc0b100 100644 --- a/google/resource_compute_region_url_map_test.go +++ b/google/resource_compute_region_url_map_test.go @@ -1 +1,854 @@ package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccComputeRegionUrlMap_update_path_matcher(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_basic1(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionUrlMap_basic2(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionUrlMap_advanced(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_advanced1(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionUrlMap_advanced2(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionUrlMap_noPathRulesWithUpdate(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_noPathRules(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionUrlMap_basic1(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionUrlMap_ilbPathUpdate(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_ilbPath(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionUrlMap_ilbPathUpdate(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionUrlMap_ilbRouteUpdate(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_ilbRoute(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRegionUrlMap_ilbRouteUpdate(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRegionUrlMap_defaultUrlRedirect(t *testing.T) { + t.Parallel() + + randomSuffix := randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionUrlMap_defaultUrlRedirectConfig(randomSuffix), + }, + { + ResourceName: "google_compute_region_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionUrlMap_basic1(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + protocol = "HTTP" + health_checks = [google_compute_region_health_check.zero.self_link] +} + +resource "google_compute_region_health_check" "zero" { + region = "us-central1" + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} + +resource "google_compute_region_url_map" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + default_service = google_compute_region_backend_service.foobar.self_link + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.foobar.self_link + } + } + + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_basic2(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + protocol = "HTTP" + health_checks = [google_compute_region_health_check.zero.self_link] +} + +resource "google_compute_region_health_check" "zero" { + region = "us-central1" + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} + +resource "google_compute_region_url_map" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + default_service = google_compute_region_backend_service.foobar.self_link + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blip" + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = google_compute_region_backend_service.foobar.self_link + } + } + + test { + host = "mysite.com" + path = "/test" + service = google_compute_region_backend_service.foobar.self_link + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_advanced1(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + protocol = "HTTP" + health_checks = [google_compute_region_health_check.zero.self_link] +} + +resource "google_compute_region_health_check" "zero" { + region = "us-central1" + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} + +resource "google_compute_region_url_map" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + default_service = google_compute_region_backend_service.foobar.self_link + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blop" + } + + host_rule { + hosts = ["myfavoritesite.com"] + path_matcher = "blip" + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blop" + + path_rule { + paths = ["/*", "/home"] + service = google_compute_region_backend_service.foobar.self_link + } + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = google_compute_region_backend_service.foobar.self_link + } + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_advanced2(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + protocol = "HTTP" + health_checks = [google_compute_region_health_check.zero.self_link] +} + +resource "google_compute_region_health_check" "zero" { + region = "us-central1" + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} + +resource "google_compute_region_url_map" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + default_service = google_compute_region_backend_service.foobar.self_link + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blep" + } + + host_rule { + hosts = ["myfavoritesite.com"] + path_matcher = "blip" + } + + host_rule { + hosts = ["myleastfavoritesite.com"] + path_matcher = "blub" + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blep" + + path_rule { + paths = ["/home"] + service = google_compute_region_backend_service.foobar.self_link + } + + path_rule { + paths = ["/login"] + service = google_compute_region_backend_service.foobar.self_link + } + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blub" + + path_rule { + paths = ["/*", "/blub"] + service = google_compute_region_backend_service.foobar.self_link + } + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = google_compute_region_backend_service.foobar.self_link + } + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_noPathRules(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + protocol = "HTTP" + health_checks = [google_compute_region_health_check.zero.self_link] +} + +resource "google_compute_region_health_check" "zero" { + region = "us-central1" + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} + +resource "google_compute_region_url_map" "foobar" { + region = "us-central1" + name = "regionurlmap-test-%s" + default_service = google_compute_region_backend_service.foobar.self_link + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + + path_matcher { + default_service = google_compute_region_backend_service.foobar.self_link + name = "boop" + } + + test { + host = "mysite.com" + path = "/*" + service = google_compute_region_backend_service.foobar.self_link + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_ilbPath(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_url_map" "foobar" { + name = "regionurlmap-test-%s" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + route_action { + cors_policy { + allow_credentials = true + allow_headers = ["Allowed content"] + allow_methods = ["GET"] + allow_origins = ["Allowed origin"] + expose_headers = ["Exposed header"] + max_age = 30 + disabled = false + } + fault_injection_policy { + abort { + http_status = 234 + percentage = 5.6 + } + delay { + fixed_delay { + seconds = 0 + nanos = 50000 + } + percentage = 7.8 + } + } + request_mirror_policy { + backend_service = google_compute_region_backend_service.home.self_link + } + retry_policy { + num_retries = 4 + per_try_timeout { + seconds = 30 + } + retry_conditions = ["5xx", "deadline-exceeded"] + } + timeout { + seconds = 20 + nanos = 750000000 + } + url_rewrite { + host_rewrite = "A replacement header" + path_prefix_rewrite = "A replacement path" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 400 + header_action { + request_headers_to_remove = ["RemoveMe"] + request_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = true + } + response_headers_to_remove = ["RemoveMe"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "regionurlmap-test-%s" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_ilbPathUpdate(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_url_map" "foobar" { + name = "regionurlmap-test-%s" + description = "a description" + default_service = google_compute_region_backend_service.home2.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths2" + } + + path_matcher { + name = "allpaths2" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home2"] + route_action { + cors_policy { + allow_credentials = true + allow_headers = ["Allowed content again"] + allow_methods = ["PUT"] + allow_origins = ["Allowed origin again"] + expose_headers = ["Exposed header again"] + max_age = 31 + disabled = true + } + fault_injection_policy { + abort { + http_status = 345 + percentage = 6.7 + } + delay { + fixed_delay { + seconds = 1 + nanos = 51000 + } + percentage = 8.9 + } + } + request_mirror_policy { + backend_service = google_compute_region_backend_service.home.self_link + } + retry_policy { + num_retries = 6 + per_try_timeout { + seconds = 31 + } + retry_conditions = ["5xx"] + } + timeout { + seconds = 21 + nanos = 760000000 + } + url_rewrite { + host_rewrite = "A replacement header again" + path_prefix_rewrite = "A replacement path again" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 401 + header_action { + request_headers_to_remove = ["RemoveMe2"] + request_headers_to_add { + header_name = "AddMe2" + header_value = "MyValue2" + replace = false + } + response_headers_to_remove = ["RemoveMe2"] + response_headers_to_add { + header_name = "AddMe2" + header_value = "MyValue2" + replace = true + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "regionurlmap-test-%s" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_backend_service" "home2" { + name = "regionurlmap-test-%s-2" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} +`, randomSuffix, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_ilbRoute(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_url_map" "foobar" { + name = "regionurlmap-test-%s" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + route_rules { + priority = 1 + header_action { + request_headers_to_remove = ["RemoveMe2"] + request_headers_to_add { + header_name = "AddSomethingElse" + header_value = "MyOtherValue" + replace = true + } + response_headers_to_remove = ["RemoveMe3"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + match_rules { + full_path_match = "a full path" + header_matches { + header_name = "someheader" + exact_match = "match this exactly" + invert_match = true + } + ignore_case = true + metadata_filters { + filter_match_criteria = "MATCH_ANY" + filter_labels { + name = "PLANET" + value = "MARS" + } + } + query_parameter_matches { + name = "a query parameter" + present_match = true + } + } + url_redirect { + host_redirect = "A host" + https_redirect = false + path_redirect = "some/path" + redirect_response_code = "TEMPORARY_REDIRECT" + strip_query = true + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "regionurlmap-test-%s" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} +`, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_ilbRouteUpdate(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_url_map" "foobar" { + name = "regionurlmap-test-%s" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths2" + } + + path_matcher { + name = "allpaths2" + default_service = google_compute_region_backend_service.home2.self_link + + route_rules { + priority = 2 + header_action { + request_headers_to_remove = ["RemoveMe2Again"] + request_headers_to_add { + header_name = "AddSomethingElseAgain" + header_value = "MyOtherValueAgain" + replace = false + } + response_headers_to_remove = ["RemoveMe3Again"] + response_headers_to_add { + header_name = "AddMeAgain" + header_value = "MyValueAgain" + replace = true + } + } + match_rules { + full_path_match = "a full path again" + header_matches { + header_name = "someheaderagain" + exact_match = "match this exactly again" + invert_match = false + } + ignore_case = false + metadata_filters { + filter_match_criteria = "MATCH_ALL" + filter_labels { + name = "PLANET" + value = "JUPITER" + } + } + } + url_redirect { + host_redirect = "A hosti again" + https_redirect = true + path_redirect = "some/path/again" + redirect_response_code = "TEMPORARY_REDIRECT" + strip_query = false + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "regionurlmap-test-%s" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_backend_service" "home2" { + name = "regionurlmap-test-%s-2" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "regionurlmap-test-%s" + http_health_check { + port = 80 + } +} +`, randomSuffix, randomSuffix, randomSuffix, randomSuffix) +} + +func testAccComputeRegionUrlMap_defaultUrlRedirectConfig(randomSuffix string) string { + return fmt.Sprintf(` +resource "google_compute_region_url_map" "foobar" { + name = "urlmap-test-%s" + default_url_redirect { + https_redirect = true + } +} +`, randomSuffix) +} diff --git a/website/docs/r/compute_backend_service.html.markdown b/website/docs/r/compute_backend_service.html.markdown index c41f567f8a0..d237ab66f2d 100644 --- a/website/docs/r/compute_backend_service.html.markdown +++ b/website/docs/r/compute_backend_service.html.markdown @@ -170,6 +170,22 @@ The following arguments are supported: (Optional) The set of backends that serve this BackendService. Structure is documented below. +* `circuit_breakers` - + (Optional) + Settings controlling the volume of connections to a backend service. This field + is applicable only when the load_balancing_scheme is set to INTERNAL_SELF_MANAGED. Structure is documented below. + +* `consistent_hash` - + (Optional) + Consistent Hash-based load balancing can be used to provide soft session + affinity based on HTTP headers, cookies or other properties. This load balancing + policy is applicable only for HTTP connections. The affinity to a particular + destination host will be lost when one or more hosts are added/removed from the + destination service. This field specifies parameters that control consistent + hashing. This field only applies if the load_balancing_scheme is set to + INTERNAL_SELF_MANAGED. This field is only applicable when locality_lb_policy is + set to MAGLEV or RING_HASH. Structure is documented below. + * `cdn_policy` - (Optional) Cloud CDN configuration for this BackendService. Structure is documented below. @@ -179,6 +195,11 @@ The following arguments are supported: Time for which instance will be drained (not accept new connections, but still work to finish started). +* `custom_request_headers` - + (Optional) + Headers that the HTTP/S load balancer should add to proxied + requests. + * `description` - (Optional) An optional description of this resource. @@ -202,6 +223,45 @@ The following arguments are supported: * `EXTERNAL` * `INTERNAL_SELF_MANAGED` +* `locality_lb_policy` - + (Optional) + The load balancing algorithm used within the scope of the locality. + The possible values are - + ROUND_ROBIN - This is a simple policy in which each healthy backend + is selected in round robin order. + LEAST_REQUEST - An O(1) algorithm which selects two random healthy + hosts and picks the host which has fewer active requests. + RING_HASH - The ring/modulo hash load balancer implements consistent + hashing to backends. The algorithm has the property that the + addition/removal of a host from a set of N hosts only affects + 1/N of the requests. + RANDOM - The load balancer selects a random healthy host. + ORIGINAL_DESTINATION - Backend host is selected based on the client + connection metadata, i.e., connections are opened + to the same address as the destination address of + the incoming connection before the connection + was redirected to the load balancer. + MAGLEV - used as a drop in replacement for the ring hash load balancer. + Maglev is not as stable as ring hash but has faster table lookup + build times and host selection times. For more information about + Maglev, refer to https://ai.google/research/pubs/pub44824 + This field is applicable only when the load_balancing_scheme is set to + INTERNAL_SELF_MANAGED. + + Possible values are: + * `ROUND_ROBIN` + * `LEAST_REQUEST` + * `RING_HASH` + * `RANDOM` + * `ORIGINAL_DESTINATION` + * `MAGLEV` + +* `outlier_detection` - + (Optional) + Settings controlling eviction of unhealthy hosts from the load balancing pool. + This field is applicable only when the load_balancing_scheme is set + to INTERNAL_SELF_MANAGED. Structure is documented below. + * `port_name` - (Optional) Name of backend port. The same name should appear in the instance @@ -244,6 +304,11 @@ The following arguments are supported: How many seconds to wait for the backend before considering it a failed request. Default is 30 seconds. Valid range is [1, 86400]. +* `log_config` - + (Optional) + This field denotes the logging options for the load balancer traffic served by this backend service. + If logging is enabled, logs will be exported to Stackdriver. Structure is documented below. + * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. @@ -348,6 +413,88 @@ The `backend` block supports: CPU utilization target for the group. The default is 0.8. Valid range is [0.0, 1.0]. +The `circuit_breakers` block supports: + +* `max_requests_per_connection` - + (Optional) + Maximum requests for a single backend connection. This parameter + is respected by both the HTTP/1.1 and HTTP/2 implementations. If + not specified, there is no limit. Setting this parameter to 1 + will effectively disable keep alive. + +* `max_connections` - + (Optional) + The maximum number of connections to the backend cluster. + Defaults to 1024. + +* `max_pending_requests` - + (Optional) + The maximum number of pending requests to the backend cluster. + Defaults to 1024. + +* `max_requests` - + (Optional) + The maximum number of parallel requests to the backend cluster. + Defaults to 1024. + +* `max_retries` - + (Optional) + The maximum number of parallel retries to the backend cluster. + Defaults to 3. + +The `consistent_hash` block supports: + +* `http_cookie` - + (Optional) + Hash is based on HTTP Cookie. This field describes a HTTP cookie + that will be used as the hash key for the consistent hash load + balancer. If the cookie is not present, it will be generated. + This field is applicable if the sessionAffinity is set to HTTP_COOKIE. Structure is documented below. + +* `http_header_name` - + (Optional) + The hash based on the value of the specified header field. + This field is applicable if the sessionAffinity is set to HEADER_FIELD. + +* `minimum_ring_size` - + (Optional) + The minimum number of virtual nodes to use for the hash ring. + Larger ring sizes result in more granular load + distributions. If the number of hosts in the load balancing pool + is larger than the ring size, each host will be assigned a single + virtual node. + Defaults to 1024. + + +The `http_cookie` block supports: + +* `ttl` - + (Optional) + Lifetime of the cookie. Structure is documented below. + +* `name` - + (Optional) + Name of the cookie. + +* `path` - + (Optional) + Path to set for the cookie. + + +The `ttl` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. + Must be from 0 to 315,576,000,000 inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond + resolution. Durations less than one second are represented + with a 0 seconds field and a positive nanos field. Must + be from 0 to 999,999,999 inclusive. + The `cdn_policy` block supports: * `cache_key_policy` - @@ -415,6 +562,118 @@ The `iap` block supports: * `oauth2_client_secret_sha256` - OAuth2 Client Secret SHA-256 for IAP **Note**: This property is sensitive and will not be displayed in the plan. +The `outlier_detection` block supports: + +* `base_ejection_time` - + (Optional) + The base time that a host is ejected for. The real time is equal to the base + time multiplied by the number of times the host has been ejected. Defaults to + 30000ms or 30s. Structure is documented below. + +* `consecutive_errors` - + (Optional) + Number of errors before a host is ejected from the connection pool. When the + backend host is accessed over HTTP, a 5xx return code qualifies as an error. + Defaults to 5. + +* `consecutive_gateway_failure` - + (Optional) + The number of consecutive gateway failures (502, 503, 504 status or connection + errors that are mapped to one of those status codes) before a consecutive + gateway failure ejection occurs. Defaults to 5. + +* `enforcing_consecutive_errors` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through consecutive 5xx. This setting can be used to disable + ejection or to ramp it up slowly. Defaults to 100. + +* `enforcing_consecutive_gateway_failure` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through consecutive gateway failures. This setting can be + used to disable ejection or to ramp it up slowly. Defaults to 0. + +* `enforcing_success_rate` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through success rate statistics. This setting can be used to + disable ejection or to ramp it up slowly. Defaults to 100. + +* `interval` - + (Optional) + Time interval between ejection sweep analysis. This can result in both new + ejections as well as hosts being returned to service. Defaults to 10 seconds. Structure is documented below. + +* `max_ejection_percent` - + (Optional) + Maximum percentage of hosts in the load balancing pool for the backend service + that can be ejected. Defaults to 10%. + +* `success_rate_minimum_hosts` - + (Optional) + The number of hosts in a cluster that must have enough request volume to detect + success rate outliers. If the number of hosts is less than this setting, outlier + detection via success rate statistics is not performed for any host in the + cluster. Defaults to 5. + +* `success_rate_request_volume` - + (Optional) + The minimum number of total requests that must be collected in one interval (as + defined by the interval duration above) to include this host in success rate + based outlier detection. If the volume is lower than this setting, outlier + detection via success rate statistics is not performed for that host. Defaults + to 100. + +* `success_rate_stdev_factor` - + (Optional) + This factor is used to determine the ejection threshold for success rate outlier + ejection. The ejection threshold is the difference between the mean success + rate, and the product of this factor and the standard deviation of the mean + success rate: mean - (stdev * success_rate_stdev_factor). This factor is divided + by a thousand to get a double. That is, if the desired factor is 1.9, the + runtime value should be 1900. Defaults to 1900. + + +The `base_ejection_time` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +The `interval` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +The `log_config` block supports: + +* `enable` - + (Optional) + Whether to enable logging for the load balancer traffic served by this backend service. + +* `sample_rate` - + (Optional) + This field can only be specified if logging is enabled for this backend service. The value of + the field must be in [0, 1]. This configures the sampling rate of requests to the load balancer + where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. + The default value is 1.0. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: diff --git a/website/docs/r/compute_region_backend_service.html.markdown b/website/docs/r/compute_region_backend_service.html.markdown index 5a6bc8564ab..cc5b2c2f46c 100644 --- a/website/docs/r/compute_region_backend_service.html.markdown +++ b/website/docs/r/compute_region_backend_service.html.markdown @@ -70,8 +70,6 @@ resource "google_compute_health_check" "default" { ```hcl resource "google_compute_region_backend_service" "default" { - provider = "google-beta" - region = "us-central1" name = "region-service" health_checks = ["${google_compute_health_check.health_check.self_link}"] @@ -81,8 +79,6 @@ resource "google_compute_region_backend_service" "default" { } resource "google_compute_health_check" "health_check" { - provider = "google-beta" - name = "rbs-health-check" http_health_check { port = 80 @@ -99,8 +95,6 @@ resource "google_compute_health_check" "health_check" { ```hcl resource "google_compute_region_backend_service" "default" { - provider = "google-beta" - region = "us-central1" name = "region-service" health_checks = ["${google_compute_health_check.health_check.self_link}"] @@ -126,8 +120,6 @@ resource "google_compute_region_backend_service" "default" { } resource "google_compute_health_check" "health_check" { - provider = "google-beta" - name = "rbs-health-check" http_health_check { port = 80 @@ -144,8 +136,6 @@ resource "google_compute_health_check" "health_check" { ```hcl resource "google_compute_region_backend_service" "default" { - provider = google-beta - load_balancing_scheme = "INTERNAL_MANAGED" backend { @@ -163,15 +153,11 @@ resource "google_compute_region_backend_service" "default" { } data "google_compute_image" "debian_image" { - provider = google-beta - family = "debian-9" project = "debian-cloud" } resource "google_compute_region_instance_group_manager" "rigm" { - provider = google-beta - region = "us-central1" name = "rbs-rigm" version { @@ -183,8 +169,6 @@ resource "google_compute_region_instance_group_manager" "rigm" { } resource "google_compute_instance_template" "instance_template" { - provider = google-beta - name = "template-region-service" machine_type = "n1-standard-1" @@ -203,8 +187,6 @@ resource "google_compute_instance_template" "instance_template" { } resource "google_compute_region_health_check" "default" { - provider = google-beta - region = "us-central1" name = "rbs-health-check" http_health_check { @@ -213,16 +195,12 @@ resource "google_compute_region_health_check" "default" { } resource "google_compute_network" "default" { - provider = google-beta - name = "rbs-net" auto_create_subnetworks = false routing_mode = "REGIONAL" } resource "google_compute_subnetwork" "default" { - provider = google-beta - name = "rbs-net-default" ip_cidr_range = "10.1.2.0/24" region = "us-central1" @@ -255,10 +233,37 @@ The following arguments are supported: - - - +* `affinity_cookie_ttl_sec` - + (Optional) + Lifetime of cookies in seconds if session_affinity is + GENERATED_COOKIE. If set to 0, the cookie is non-persistent and lasts + only until the end of the browser session (or equivalent). The + maximum allowed value for TTL is one day. + When the load balancing scheme is INTERNAL, this field is not used. + * `backend` - (Optional) The set of backends that serve this RegionBackendService. Structure is documented below. +* `circuit_breakers` - + (Optional) + Settings controlling the volume of connections to a backend service. This field + is applicable only when the `load_balancing_scheme` is set to INTERNAL_MANAGED + and the `protocol` is set to HTTP, HTTPS, or HTTP2. Structure is documented below. + +* `consistent_hash` - + (Optional) + Consistent Hash-based load balancing can be used to provide soft session + affinity based on HTTP headers, cookies or other properties. This load balancing + policy is applicable only for HTTP connections. The affinity to a particular + destination host will be lost when one or more hosts are added/removed from the + destination service. This field specifies parameters that control consistent + hashing. + This field only applies when all of the following are true - + * `load_balancing_scheme` is set to INTERNAL_MANAGED + * `protocol` is set to HTTP, HTTPS, or HTTP2 + * `locality_lb_policy` is set to MAGLEV or RING_HASH Structure is documented below. + * `connection_draining_timeout_sec` - (Optional) Time for which instance will be drained (not accept new @@ -268,6 +273,10 @@ The following arguments are supported: (Optional) An optional description of this resource. +* `failover_policy` - + (Optional) + Policy for failovers. Structure is documented below. + * `load_balancing_scheme` - (Optional) Indicates what kind of load balancing this regional backend service @@ -279,6 +288,45 @@ The following arguments are supported: * `INTERNAL` * `INTERNAL_MANAGED` +* `locality_lb_policy` - + (Optional) + The load balancing algorithm used within the scope of the locality. + The possible values are - + ROUND_ROBIN - This is a simple policy in which each healthy backend + is selected in round robin order. + LEAST_REQUEST - An O(1) algorithm which selects two random healthy + hosts and picks the host which has fewer active requests. + RING_HASH - The ring/modulo hash load balancer implements consistent + hashing to backends. The algorithm has the property that the + addition/removal of a host from a set of N hosts only affects + 1/N of the requests. + RANDOM - The load balancer selects a random healthy host. + ORIGINAL_DESTINATION - Backend host is selected based on the client + connection metadata, i.e., connections are opened + to the same address as the destination address of + the incoming connection before the connection + was redirected to the load balancer. + MAGLEV - used as a drop in replacement for the ring hash load balancer. + Maglev is not as stable as ring hash but has faster table lookup + build times and host selection times. For more information about + Maglev, refer to https://ai.google/research/pubs/pub44824 + This field is applicable only when the `load_balancing_scheme` is set to + INTERNAL_MANAGED and the `protocol` is set to HTTP, HTTPS, or HTTP2. + + Possible values are: + * `ROUND_ROBIN` + * `LEAST_REQUEST` + * `RING_HASH` + * `RANDOM` + * `ORIGINAL_DESTINATION` + * `MAGLEV` + +* `outlier_detection` - + (Optional) + Settings controlling eviction of unhealthy hosts from the load balancing pool. + This field is applicable only when the `load_balancing_scheme` is set + to INTERNAL_MANAGED and the `protocol` is set to HTTP, HTTPS, or HTTP2. Structure is documented below. + * `protocol` - (Optional) The protocol this RegionBackendService uses to communicate with backends. @@ -312,6 +360,11 @@ The following arguments are supported: How many seconds to wait for the backend before considering it a failed request. Default is 30 seconds. Valid range is [1, 86400]. +* `log_config` - + (Optional) + This field denotes the logging options for the load balancer traffic served by this backend service. + If logging is enabled, logs will be exported to Stackdriver. Structure is documented below. + * `network` - (Optional) The URL of the network to which this backend service belongs. @@ -354,6 +407,11 @@ The `backend` block supports: An optional description of this resource. Provide this property when you create the resource. +* `failover` - + (Optional) + This field designates whether this is a failover backend. More + than one failover backend can be configured for a given RegionBackendService. + * `group` - (Required) The fully-qualified URL of an Instance Group or Network Endpoint @@ -433,6 +491,231 @@ The `backend` block supports: CPU utilization target for the group. Valid range is [0.0, 1.0]. Cannot be set for INTERNAL backend services. +The `circuit_breakers` block supports: + +* `max_requests_per_connection` - + (Optional) + Maximum requests for a single backend connection. This parameter + is respected by both the HTTP/1.1 and HTTP/2 implementations. If + not specified, there is no limit. Setting this parameter to 1 + will effectively disable keep alive. + +* `max_connections` - + (Optional) + The maximum number of connections to the backend cluster. + Defaults to 1024. + +* `max_pending_requests` - + (Optional) + The maximum number of pending requests to the backend cluster. + Defaults to 1024. + +* `max_requests` - + (Optional) + The maximum number of parallel requests to the backend cluster. + Defaults to 1024. + +* `max_retries` - + (Optional) + The maximum number of parallel retries to the backend cluster. + Defaults to 3. + +The `consistent_hash` block supports: + +* `http_cookie` - + (Optional) + Hash is based on HTTP Cookie. This field describes a HTTP cookie + that will be used as the hash key for the consistent hash load + balancer. If the cookie is not present, it will be generated. + This field is applicable if the sessionAffinity is set to HTTP_COOKIE. Structure is documented below. + +* `http_header_name` - + (Optional) + The hash based on the value of the specified header field. + This field is applicable if the sessionAffinity is set to HEADER_FIELD. + +* `minimum_ring_size` - + (Optional) + The minimum number of virtual nodes to use for the hash ring. + Larger ring sizes result in more granular load + distributions. If the number of hosts in the load balancing pool + is larger than the ring size, each host will be assigned a single + virtual node. + Defaults to 1024. + + +The `http_cookie` block supports: + +* `ttl` - + (Optional) + Lifetime of the cookie. Structure is documented below. + +* `name` - + (Optional) + Name of the cookie. + +* `path` - + (Optional) + Path to set for the cookie. + + +The `ttl` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. + Must be from 0 to 315,576,000,000 inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond + resolution. Durations less than one second are represented + with a 0 seconds field and a positive nanos field. Must + be from 0 to 999,999,999 inclusive. + +The `failover_policy` block supports: + +* `disable_connection_drain_on_failover` - + (Optional) + On failover or failback, this field indicates whether connection drain + will be honored. Setting this to true has the following effect: connections + to the old active pool are not drained. Connections to the new active pool + use the timeout of 10 min (currently fixed). Setting to false has the + following effect: both old and new connections will have a drain timeout + of 10 min. + This can be set to true only if the protocol is TCP. + The default is false. + +* `drop_traffic_if_unhealthy` - + (Optional) + This option is used only when no healthy VMs are detected in the primary + and backup instance groups. When set to true, traffic is dropped. When + set to false, new connections are sent across all VMs in the primary group. + The default is false. + +* `failover_ratio` - + (Optional) + The value of the field must be in [0, 1]. If the ratio of the healthy + VMs in the primary backend is at or below this number, traffic arriving + at the load-balanced IP will be directed to the failover backend. + In case where 'failoverRatio' is not set or all the VMs in the backup + backend are unhealthy, the traffic will be directed back to the primary + backend in the "force" mode, where traffic will be spread to the healthy + VMs with the best effort, or to all VMs when no VM is healthy. + This field is only used with l4 load balancing. + +The `outlier_detection` block supports: + +* `base_ejection_time` - + (Optional) + The base time that a host is ejected for. The real time is equal to the base + time multiplied by the number of times the host has been ejected. Defaults to + 30000ms or 30s. Structure is documented below. + +* `consecutive_errors` - + (Optional) + Number of errors before a host is ejected from the connection pool. When the + backend host is accessed over HTTP, a 5xx return code qualifies as an error. + Defaults to 5. + +* `consecutive_gateway_failure` - + (Optional) + The number of consecutive gateway failures (502, 503, 504 status or connection + errors that are mapped to one of those status codes) before a consecutive + gateway failure ejection occurs. Defaults to 5. + +* `enforcing_consecutive_errors` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through consecutive 5xx. This setting can be used to disable + ejection or to ramp it up slowly. Defaults to 100. + +* `enforcing_consecutive_gateway_failure` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through consecutive gateway failures. This setting can be + used to disable ejection or to ramp it up slowly. Defaults to 0. + +* `enforcing_success_rate` - + (Optional) + The percentage chance that a host will be actually ejected when an outlier + status is detected through success rate statistics. This setting can be used to + disable ejection or to ramp it up slowly. Defaults to 100. + +* `interval` - + (Optional) + Time interval between ejection sweep analysis. This can result in both new + ejections as well as hosts being returned to service. Defaults to 10 seconds. Structure is documented below. + +* `max_ejection_percent` - + (Optional) + Maximum percentage of hosts in the load balancing pool for the backend service + that can be ejected. Defaults to 10%. + +* `success_rate_minimum_hosts` - + (Optional) + The number of hosts in a cluster that must have enough request volume to detect + success rate outliers. If the number of hosts is less than this setting, outlier + detection via success rate statistics is not performed for any host in the + cluster. Defaults to 5. + +* `success_rate_request_volume` - + (Optional) + The minimum number of total requests that must be collected in one interval (as + defined by the interval duration above) to include this host in success rate + based outlier detection. If the volume is lower than this setting, outlier + detection via success rate statistics is not performed for that host. Defaults + to 100. + +* `success_rate_stdev_factor` - + (Optional) + This factor is used to determine the ejection threshold for success rate outlier + ejection. The ejection threshold is the difference between the mean success + rate, and the product of this factor and the standard deviation of the mean + success rate: mean - (stdev * success_rate_stdev_factor). This factor is divided + by a thousand to get a double. That is, if the desired factor is 1.9, the + runtime value should be 1900. Defaults to 1900. + + +The `base_ejection_time` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +The `interval` block supports: + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +The `log_config` block supports: + +* `enable` - + (Optional) + Whether to enable logging for the load balancer traffic served by this backend service. + +* `sample_rate` - + (Optional) + This field can only be specified if logging is enabled for this backend service. The value of + the field must be in [0, 1]. This configures the sampling rate of requests to the load balancer + where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. + The default value is 1.0. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: diff --git a/website/docs/r/compute_region_disk.html.markdown b/website/docs/r/compute_region_disk.html.markdown index be2e302c765..0a7d9108f39 100644 --- a/website/docs/r/compute_region_disk.html.markdown +++ b/website/docs/r/compute_region_disk.html.markdown @@ -42,7 +42,7 @@ affordable storage with consistent performance characteristics. To get more information about RegionDisk, see: -* [API documentation](https://cloud.google.com/compute/docs/reference/rest/beta/regionDisks) +* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/regionDisks) * How-to Guides * [Adding or Resizing Regional Persistent Disks](https://cloud.google.com/compute/docs/disks/regional-persistent-disk) diff --git a/website/docs/r/compute_region_health_check.html.markdown b/website/docs/r/compute_region_health_check.html.markdown index 5dc53d6a298..f4051308c24 100644 --- a/website/docs/r/compute_region_health_check.html.markdown +++ b/website/docs/r/compute_region_health_check.html.markdown @@ -37,7 +37,7 @@ healthy again and can receive new connections. To get more information about RegionHealthCheck, see: -* [API documentation](https://cloud.google.com/compute/docs/reference/rest/beta/regionHealthChecks) +* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/regionHealthChecks) * How-to Guides * [Official Documentation](https://cloud.google.com/load-balancing/docs/health-checks) diff --git a/website/docs/r/compute_region_ssl_certificate.html.markdown b/website/docs/r/compute_region_ssl_certificate.html.markdown index 9af75d43bae..91a8089b685 100644 --- a/website/docs/r/compute_region_ssl_certificate.html.markdown +++ b/website/docs/r/compute_region_ssl_certificate.html.markdown @@ -112,7 +112,6 @@ resource "random_id" "certificate" { // name with name_prefix, or use random_id resource. Example: resource "google_compute_region_ssl_certificate" "default" { - provider = google-beta region = "us-central1" name_prefix = "my-certificate-" private_key = file("path/to/private.key") @@ -124,7 +123,6 @@ resource "google_compute_region_ssl_certificate" "default" { } resource "google_compute_region_target_https_proxy" "default" { - provider = google-beta region = "us-central1" name = "test-proxy" url_map = google_compute_region_url_map.default.self_link @@ -132,7 +130,6 @@ resource "google_compute_region_target_https_proxy" "default" { } resource "google_compute_region_url_map" "default" { - provider = google-beta region = "us-central1" name = "url-map" description = "a description" @@ -156,7 +153,6 @@ resource "google_compute_region_url_map" "default" { } resource "google_compute_region_backend_service" "default" { - provider = google-beta region = "us-central1" name = "backend-service" protocol = "HTTP" @@ -166,7 +162,6 @@ resource "google_compute_region_backend_service" "default" { } resource "google_compute_region_health_check" "default" { - provider = google-beta region = "us-central1" name = "http-health-check" http_health_check { diff --git a/website/docs/r/compute_region_target_http_proxy.html.markdown b/website/docs/r/compute_region_target_http_proxy.html.markdown new file mode 100644 index 00000000000..d3877bbb0b7 --- /dev/null +++ b/website/docs/r/compute_region_target_http_proxy.html.markdown @@ -0,0 +1,189 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Compute Engine" +layout: "google" +page_title: "Google: google_compute_region_target_http_proxy" +sidebar_current: "docs-google-compute-region-target-http-proxy" +description: |- + Represents a RegionTargetHttpProxy resource, which is used by one or more + forwarding rules to route incoming HTTP requests to a URL map. +--- + +# google\_compute\_region\_target\_http\_proxy + +Represents a RegionTargetHttpProxy resource, which is used by one or more +forwarding rules to route incoming HTTP requests to a URL map. + + +To get more information about RegionTargetHttpProxy, see: + +* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/regionTargetHttpProxies) +* How-to Guides + * [Official Documentation](https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) + + +## Example Usage - Region Target Http Proxy Basic + + +```hcl +resource "google_compute_region_target_http_proxy" "default" { + region = "us-central1" + name = "test-proxy" + url_map = google_compute_region_url_map.default.self_link +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "url-map" + default_service = google_compute_region_backend_service.default.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.default.self_link + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.default.self_link + } + } +} + +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "backend-service" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "http-health-check" + http_health_check { + port = 80 + } +} +``` + +## Example Usage - Region Target Http Proxy Https Redirect + + +```hcl +resource "google_compute_region_target_http_proxy" "default" { + region = "us-central1" + name = "test-https-redirect-proxy" + url_map = google_compute_region_url_map.default.self_link +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "url-map" + default_url_redirect { + https_redirect = true + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + Name of the resource. Provided by the client when the resource is + created. The name must be 1-63 characters long, and comply with + RFC1035. Specifically, the name must be 1-63 characters long and match + the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` which means the + first character must be a lowercase letter, and all following + characters must be a dash, lowercase letter, or digit, except the last + character, which cannot be a dash. + +* `url_map` - + (Required) + A reference to the RegionUrlMap resource that defines the mapping from URL + to the BackendService. + + +- - - + + +* `description` - + (Optional) + An optional description of this resource. + +* `region` - + (Optional) + The Region in which the created target https proxy should reside. + If it is not provided, the provider region is used. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}` + +* `creation_timestamp` - + Creation timestamp in RFC3339 text format. + +* `proxy_id` - + The unique identifier for the resource. +* `self_link` - The URI of the created resource. + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 4 minutes. +- `update` - Default is 4 minutes. +- `delete` - Default is 4 minutes. + +## Import + +RegionTargetHttpProxy can be imported using any of these accepted formats: + +``` +$ terraform import google_compute_region_target_http_proxy.default projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}} +$ terraform import google_compute_region_target_http_proxy.default {{project}}/{{region}}/{{name}} +$ terraform import google_compute_region_target_http_proxy.default {{region}}/{{name}} +$ terraform import google_compute_region_target_http_proxy.default {{name}} +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/docs/r/compute_region_target_https_proxy.html.markdown b/website/docs/r/compute_region_target_https_proxy.html.markdown new file mode 100644 index 00000000000..0b02be407ee --- /dev/null +++ b/website/docs/r/compute_region_target_https_proxy.html.markdown @@ -0,0 +1,182 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Compute Engine" +layout: "google" +page_title: "Google: google_compute_region_target_https_proxy" +sidebar_current: "docs-google-compute-region-target-https-proxy" +description: |- + Represents a RegionTargetHttpsProxy resource, which is used by one or more + forwarding rules to route incoming HTTPS requests to a URL map. +--- + +# google\_compute\_region\_target\_https\_proxy + +Represents a RegionTargetHttpsProxy resource, which is used by one or more +forwarding rules to route incoming HTTPS requests to a URL map. + + +To get more information about RegionTargetHttpsProxy, see: + +* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/regionTargetHttpsProxies) +* How-to Guides + * [Official Documentation](https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) + + +## Example Usage - Region Target Https Proxy Basic + + +```hcl +resource "google_compute_region_target_https_proxy" "default" { + region = "us-central1" + name = "test-proxy" + url_map = google_compute_region_url_map.default.self_link + ssl_certificates = [google_compute_region_ssl_certificate.default.self_link] +} + +resource "google_compute_region_ssl_certificate" "default" { + region = "us-central1" + name = "my-certificate" + private_key = file("path/to/private.key") + certificate = file("path/to/certificate.crt") +} + +resource "google_compute_region_url_map" "default" { + region = "us-central1" + name = "url-map" + description = "a description" + + default_service = google_compute_region_backend_service.default.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.default.self_link + + path_rule { + paths = ["/*"] + service = google_compute_region_backend_service.default.self_link + } + } +} + +resource "google_compute_region_backend_service" "default" { + region = "us-central1" + name = "backend-service" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + name = "http-health-check" + http_health_check { + port = 80 + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + Name of the resource. Provided by the client when the resource is + created. The name must be 1-63 characters long, and comply with + RFC1035. Specifically, the name must be 1-63 characters long and match + the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` which means the + first character must be a lowercase letter, and all following + characters must be a dash, lowercase letter, or digit, except the last + character, which cannot be a dash. + +* `ssl_certificates` - + (Required) + A list of RegionSslCertificate resources that are used to authenticate + connections between users and the load balancer. Currently, exactly + one SSL certificate must be specified. + +* `url_map` - + (Required) + A reference to the RegionUrlMap resource that defines the mapping from URL + to the RegionBackendService. + + +- - - + + +* `description` - + (Optional) + An optional description of this resource. + +* `region` - + (Optional) + The Region in which the created target https proxy should reside. + If it is not provided, the provider region is used. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}` + +* `creation_timestamp` - + Creation timestamp in RFC3339 text format. + +* `proxy_id` - + The unique identifier for the resource. +* `self_link` - The URI of the created resource. + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 4 minutes. +- `update` - Default is 4 minutes. +- `delete` - Default is 4 minutes. + +## Import + +RegionTargetHttpsProxy can be imported using any of these accepted formats: + +``` +$ terraform import google_compute_region_target_https_proxy.default projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}} +$ terraform import google_compute_region_target_https_proxy.default {{project}}/{{region}}/{{name}} +$ terraform import google_compute_region_target_https_proxy.default {{region}}/{{name}} +$ terraform import google_compute_region_target_https_proxy.default {{name}} +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/docs/r/compute_region_url_map.html.markdown b/website/docs/r/compute_region_url_map.html.markdown new file mode 100644 index 00000000000..a7cc0d0c43b --- /dev/null +++ b/website/docs/r/compute_region_url_map.html.markdown @@ -0,0 +1,1795 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Compute Engine" +layout: "google" +page_title: "Google: google_compute_region_url_map" +sidebar_current: "docs-google-compute-region-url-map" +description: |- + UrlMaps are used to route requests to a backend service based on rules + that you define for the host and path of an incoming URL. +--- + +# google\_compute\_region\_url\_map + +UrlMaps are used to route requests to a backend service based on rules +that you define for the host and path of an incoming URL. + + + + +## Example Usage - Region Url Map Basic + + +```hcl +resource "google_compute_region_url_map" "regionurlmap" { + region = "us-central1" + + name = "regionurlmap" + description = "a description" + + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + service = google_compute_region_backend_service.home.self_link + } + + path_rule { + paths = ["/login"] + service = google_compute_region_backend_service.login.self_link + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "login" { + region = "us-central1" + + name = "login" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_backend_service" "home" { + region = "us-central1" + + name = "home" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] +} + +resource "google_compute_region_health_check" "default" { + region = "us-central1" + + name = "health-check" + check_interval_sec = 1 + timeout_sec = 1 + http_health_check { + port = 80 + request_path = "/" + } +} +``` + +## Example Usage - Region Url Map L7 Ilb Path + + +```hcl +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + route_action { + cors_policy { + allow_credentials = true + allow_headers = ["Allowed content"] + allow_methods = ["GET"] + allow_origins = ["Allowed origin"] + expose_headers = ["Exposed header"] + max_age = 30 + disabled = false + } + fault_injection_policy { + abort { + http_status = 234 + percentage = 5.6 + } + delay { + fixed_delay { + seconds = 0 + nanos = 50000 + } + percentage = 7.8 + } + } + request_mirror_policy { + backend_service = google_compute_region_backend_service.home.self_link + } + retry_policy { + num_retries = 4 + per_try_timeout { + seconds = 30 + } + retry_conditions = ["5xx", "deadline-exceeded"] + } + timeout { + seconds = 20 + nanos = 750000000 + } + url_rewrite { + host_rewrite = "A replacement header" + path_prefix_rewrite = "A replacement path" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 400 + header_action { + request_headers_to_remove = ["RemoveMe"] + request_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = true + } + response_headers_to_remove = ["RemoveMe"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "health-check" + http_health_check { + port = 80 + } +} +``` + +## Example Usage - Region Url Map L7 Ilb Path Partial + + +```hcl +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + path_rule { + paths = ["/home"] + route_action { + retry_policy { + num_retries = 4 + per_try_timeout { + seconds = 30 + } + retry_conditions = ["5xx", "deadline-exceeded"] + } + timeout { + seconds = 20 + nanos = 750000000 + } + url_rewrite { + host_rewrite = "A replacement header" + path_prefix_rewrite = "A replacement path" + } + weighted_backend_services { + backend_service = google_compute_region_backend_service.home.self_link + weight = 400 + header_action { + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "health-check" + http_health_check { + port = 80 + } +} +``` + +## Example Usage - Region Url Map L7 Ilb Route + + +```hcl +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + route_rules { + priority = 1 + header_action { + request_headers_to_remove = ["RemoveMe2"] + request_headers_to_add { + header_name = "AddSomethingElse" + header_value = "MyOtherValue" + replace = true + } + response_headers_to_remove = ["RemoveMe3"] + response_headers_to_add { + header_name = "AddMe" + header_value = "MyValue" + replace = false + } + } + match_rules { + full_path_match = "a full path" + header_matches { + header_name = "someheader" + exact_match = "match this exactly" + invert_match = true + } + ignore_case = true + metadata_filters { + filter_match_criteria = "MATCH_ANY" + filter_labels { + name = "PLANET" + value = "MARS" + } + } + query_parameter_matches { + name = "a query parameter" + present_match = true + } + } + url_redirect { + host_redirect = "A host" + https_redirect = false + path_redirect = "some/path" + redirect_response_code = "TEMPORARY_REDIRECT" + strip_query = true + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "health-check" + http_health_check { + port = 80 + } +} +``` + +## Example Usage - Region Url Map L7 Ilb Route Partial + + +```hcl +resource "google_compute_region_url_map" "regionurlmap" { + name = "regionurlmap" + description = "a description" + default_service = google_compute_region_backend_service.home.self_link + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + name = "allpaths" + default_service = google_compute_region_backend_service.home.self_link + + route_rules { + priority = 1 + service = google_compute_region_backend_service.home.self_link + header_action { + request_headers_to_remove = ["RemoveMe2"] + } + match_rules { + full_path_match = "a full path" + header_matches { + header_name = "someheader" + exact_match = "match this exactly" + invert_match = true + } + query_parameter_matches { + name = "a query parameter" + present_match = true + } + } + } + } + + test { + service = google_compute_region_backend_service.home.self_link + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_region_backend_service" "home" { + name = "home" + protocol = "HTTP" + timeout_sec = 10 + + health_checks = [google_compute_region_health_check.default.self_link] + load_balancing_scheme = "INTERNAL_MANAGED" +} + +resource "google_compute_region_health_check" "default" { + name = "health-check" + http_health_check { + port = 80 + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + Name of the resource. Provided by the client when the resource is + created. The name must be 1-63 characters long, and comply with + RFC1035. Specifically, the name must be 1-63 characters long and match + the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` which means the + first character must be a lowercase letter, and all following + characters must be a dash, lowercase letter, or digit, except the last + character, which cannot be a dash. + + +- - - + + +* `default_service` - + (Optional) + The full or partial URL of the defaultService resource to which traffic is directed if + none of the hostRules match. If defaultRouteAction is additionally specified, advanced + routing actions like URL Rewrites, etc. take effect prior to sending the request to the + backend. However, if defaultService is specified, defaultRouteAction cannot contain any + weightedBackendServices. Conversely, if routeAction specifies any + weightedBackendServices, service must not be specified. Only one of defaultService, + defaultUrlRedirect or defaultRouteAction.weightedBackendService must be set. + +* `description` - + (Optional) + An optional description of this resource. Provide this property when + you create the resource. + +* `host_rule` - + (Optional) + The list of HostRules to use against the URL. Structure is documented below. + +* `path_matcher` - + (Optional) + The list of named PathMatchers to use against the URL. Structure is documented below. + +* `test` - + (Optional) + The list of expected URL mappings. Requests to update this UrlMap will + succeed only if all of the test cases pass. Structure is documented below. + +* `default_url_redirect` - + (Optional) + When none of the specified hostRules match, the request is redirected to a URL specified + by defaultUrlRedirect. If defaultUrlRedirect is specified, defaultService or + defaultRouteAction must not be set. Structure is documented below. + +* `region` - + (Optional) + The Region in which the url map should reside. + If it is not provided, the provider region is used. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `host_rule` block supports: + +* `description` - + (Optional) + An optional description of this HostRule. Provide this property + when you create the resource. + +* `hosts` - + (Required) + The list of host patterns to match. They must be valid + hostnames, except * will match any string of ([a-z0-9-.]*). In + that case, * must be the first character and must be followed in + the pattern by either - or .. + +* `path_matcher` - + (Required) + The name of the PathMatcher to use to match the path portion of + the URL if the hostRule matches the URL's host portion. + +The `path_matcher` block supports: + +* `default_service` - + (Required) + A reference to a RegionBackendService resource. This will be used if + none of the pathRules defined by this PathMatcher is matched by + the URL's path portion. + +* `description` - + (Optional) + An optional description of this resource. + +* `name` - + (Required) + The name to which this PathMatcher is referred by the HostRule. + +* `route_rules` - + (Optional) + The list of ordered HTTP route rules. Use this list instead of pathRules when + advanced route matching and routing actions are desired. The order of specifying + routeRules matters: the first rule that matches will cause its specified routing + action to take effect. Within a given pathMatcher, only one of pathRules or + routeRules must be set. routeRules are not supported in UrlMaps intended for + External load balancers. Structure is documented below. + +* `path_rule` - + (Optional) + The list of path rules. Use this list instead of routeRules when routing based + on simple path matching is all that's required. The order by which path rules + are specified does not matter. Matches are always done on the longest-path-first + basis. For example: a pathRule with a path /a/b/c/* will match before /a/b/* + irrespective of the order in which those paths appear in this list. Within a + given pathMatcher, only one of pathRules or routeRules must be set. Structure is documented below. + +* `default_url_redirect` - + (Optional) + When none of the specified hostRules match, the request is redirected to a URL specified + by defaultUrlRedirect. If defaultUrlRedirect is specified, defaultService or + defaultRouteAction must not be set. Structure is documented below. + + +The `route_rules` block supports: + +* `priority` - + (Required) + For routeRules within a given pathMatcher, priority determines the order + in which load balancer will interpret routeRules. RouteRules are evaluated + in order of priority, from the lowest to highest number. The priority of + a rule decreases as its number increases (1, 2, 3, N+1). The first rule + that matches the request is applied. + You cannot configure two or more routeRules with the same priority. + Priority for each rule must be set to a number between 0 and + 2147483647 inclusive. + Priority numbers can have gaps, which enable you to add or remove rules + in the future without affecting the rest of the rules. For example, + 1, 2, 3, 4, 5, 9, 12, 16 is a valid series of priority numbers to which + you could add rules numbered from 6 to 8, 10 to 11, and 13 to 15 in the + future without any impact on existing rules. + +* `service` - + (Optional) + The region backend service resource to which traffic is + directed if this rule is matched. If routeAction is additionally specified, + advanced routing actions like URL Rewrites, etc. take effect prior to sending + the request to the backend. However, if service is specified, routeAction cannot + contain any weightedBackendService s. Conversely, if routeAction specifies any + weightedBackendServices, service must not be specified. Only one of urlRedirect, + service or routeAction.weightedBackendService must be set. + +* `header_action` - + (Optional) + Specifies changes to request and response headers that need to take effect for + the selected backendService. The headerAction specified here are applied before + the matching pathMatchers[].headerAction and after pathMatchers[].routeRules[].r + outeAction.weightedBackendService.backendServiceWeightAction[].headerAction Structure is documented below. + +* `match_rules` - + (Optional) + The rules for determining a match. Structure is documented below. + +* `route_action` - + (Optional) + In response to a matching matchRule, the load balancer performs advanced routing + actions like URL rewrites, header transformations, etc. prior to forwarding the + request to the selected backend. If routeAction specifies any + weightedBackendServices, service must not be set. Conversely if service is set, + routeAction cannot contain any weightedBackendServices. Only one of routeAction + or urlRedirect must be set. Structure is documented below. + +* `url_redirect` - + (Optional) + When this rule is matched, the request is redirected to a URL specified by + urlRedirect. If urlRedirect is specified, service or routeAction must not be + set. Structure is documented below. + + +The `header_action` block supports: + +* `request_headers_to_add` - + (Optional) + Headers to add to a matching request prior to forwarding the request to the + backendService. Structure is documented below. + +* `request_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the request + prior to forwarding the request to the backendService. + +* `response_headers_to_add` - + (Optional) + Headers to add the response prior to sending the response back to the client. Structure is documented below. + +* `response_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the response + prior to sending the response back to the client. + + +The `request_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `response_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `match_rules` block supports: + +* `full_path_match` - + (Optional) + For satifying the matchRule condition, the path of the request must exactly + match the value specified in fullPathMatch after removing any query parameters + and anchor that may be part of the original URL. FullPathMatch must be between 1 + and 1024 characters. Only one of prefixMatch, fullPathMatch or regexMatch must + be specified. + +* `header_matches` - + (Optional) + Specifies a list of header match criteria, all of which must match corresponding + headers in the request. Structure is documented below. + +* `ignore_case` - + (Optional) + Specifies that prefixMatch and fullPathMatch matches are case sensitive. + Defaults to false. + +* `metadata_filters` - + (Optional) + Opaque filter criteria used by Loadbalancer to restrict routing configuration to + a limited set xDS compliant clients. In their xDS requests to Loadbalancer, xDS + clients present node metadata. If a match takes place, the relevant routing + configuration is made available to those proxies. For each metadataFilter in + this list, if its filterMatchCriteria is set to MATCH_ANY, at least one of the + filterLabels must match the corresponding label provided in the metadata. If its + filterMatchCriteria is set to MATCH_ALL, then all of its filterLabels must match + with corresponding labels in the provided metadata. metadataFilters specified + here can be overrides those specified in ForwardingRule that refers to this + UrlMap. metadataFilters only applies to Loadbalancers that have their + loadBalancingScheme set to INTERNAL_SELF_MANAGED. Structure is documented below. + +* `prefix_match` - + (Optional) + For satifying the matchRule condition, the request's path must begin with the + specified prefixMatch. prefixMatch must begin with a /. The value must be + between 1 and 1024 characters. Only one of prefixMatch, fullPathMatch or + regexMatch must be specified. + +* `query_parameter_matches` - + (Optional) + Specifies a list of query parameter match criteria, all of which must match + corresponding query parameters in the request. Structure is documented below. + +* `regex_match` - + (Optional) + For satifying the matchRule condition, the path of the request must satisfy the + regular expression specified in regexMatch after removing any query parameters + and anchor supplied with the original URL. For regular expression grammar please + see en.cppreference.com/w/cpp/regex/ecmascript Only one of prefixMatch, + fullPathMatch or regexMatch must be specified. + + +The `header_matches` block supports: + +* `exact_match` - + (Optional) + The value should exactly match contents of exactMatch. Only one of exactMatch, + prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch must be set. + +* `header_name` - + (Required) + The name of the HTTP header to match. For matching against the HTTP request's + authority, use a headerMatch with the header name ":authority". For matching a + request's method, use the headerName ":method". + +* `invert_match` - + (Optional) + If set to false, the headerMatch is considered a match if the match criteria + above are met. If set to true, the headerMatch is considered a match if the + match criteria above are NOT met. Defaults to false. + +* `prefix_match` - + (Optional) + The value of the header must start with the contents of prefixMatch. Only one of + exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch + must be set. + +* `present_match` - + (Optional) + A header with the contents of headerName must exist. The match takes place + whether or not the request's header has a value or not. Only one of exactMatch, + prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch must be set. + +* `range_match` - + (Optional) + The header value must be an integer and its value must be in the range specified + in rangeMatch. If the header does not contain an integer, number or is empty, + the match fails. For example for a range [-5, 0] - -3 will match. - 0 will + not match. - 0.25 will not match. - -3someString will not match. Only one of + exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch + must be set. Structure is documented below. + +* `regex_match` - + (Optional) + The value of the header must match the regualar expression specified in + regexMatch. For regular expression grammar, please see: + en.cppreference.com/w/cpp/regex/ecmascript For matching against a port + specified in the HTTP request, use a headerMatch with headerName set to PORT and + a regular expression that satisfies the RFC2616 Host header's port specifier. + Only one of exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or + rangeMatch must be set. + +* `suffix_match` - + (Optional) + The value of the header must end with the contents of suffixMatch. Only one of + exactMatch, prefixMatch, suffixMatch, regexMatch, presentMatch or rangeMatch + must be set. + + +The `range_match` block supports: + +* `range_end` - + (Required) + The end of the range (exclusive). + +* `range_start` - + (Required) + The start of the range (inclusive). + +The `metadata_filters` block supports: + +* `filter_labels` - + (Required) + The list of label value pairs that must match labels in the provided metadata + based on filterMatchCriteria This list must not be empty and can have at the + most 64 entries. Structure is documented below. + +* `filter_match_criteria` - + (Required) + Specifies how individual filterLabel matches within the list of filterLabels + contribute towards the overall metadataFilter match. Supported values are: + - MATCH_ANY: At least one of the filterLabels must have a matching label in the + provided metadata. + - MATCH_ALL: All filterLabels must have matching labels in + the provided metadata. + + Possible values are: + * `MATCH_ALL` + * `MATCH_ANY` + + +The `filter_labels` block supports: + +* `name` - + (Required) + Name of metadata label. The name can have a maximum length of 1024 characters + and must be at least 1 character long. + +* `value` - + (Required) + The value of the label must match the specified value. value can have a maximum + length of 1024 characters. + +The `query_parameter_matches` block supports: + +* `exact_match` - + (Optional) + The queryParameterMatch matches if the value of the parameter exactly matches + the contents of exactMatch. Only one of presentMatch, exactMatch and regexMatch + must be set. + +* `name` - + (Required) + The name of the query parameter to match. The query parameter must exist in the + request, in the absence of which the request match fails. + +* `present_match` - + (Optional) + Specifies that the queryParameterMatch matches if the request contains the query + parameter, irrespective of whether the parameter has a value or not. Only one of + presentMatch, exactMatch and regexMatch must be set. + +* `regex_match` - + (Optional) + The queryParameterMatch matches if the value of the parameter matches the + regular expression specified by regexMatch. For the regular expression grammar, + please see en.cppreference.com/w/cpp/regex/ecmascript Only one of presentMatch, + exactMatch and regexMatch must be set. + +The `route_action` block supports: + +* `cors_policy` - + (Optional) + The specification for allowing client side cross-origin requests. Please see W3C + Recommendation for Cross Origin Resource Sharing Structure is documented below. + +* `fault_injection_policy` - + (Optional) + The specification for fault injection introduced into traffic to test the + resiliency of clients to backend service failure. As part of fault injection, + when clients send requests to a backend service, delays can be introduced by + Loadbalancer on a percentage of requests before sending those request to the + backend service. Similarly requests from clients can be aborted by the + Loadbalancer for a percentage of requests. timeout and retry_policy will be + ignored by clients that are configured with a fault_injection_policy. Structure is documented below. + +* `request_mirror_policy` - + (Optional) + Specifies the policy on how requests intended for the route's backends are + shadowed to a separate mirrored backend service. Loadbalancer does not wait for + responses from the shadow service. Prior to sending traffic to the shadow + service, the host / authority header is suffixed with -shadow. Structure is documented below. + +* `retry_policy` - + (Optional) + Specifies the retry policy associated with this route. Structure is documented below. + +* `timeout` - + (Optional) + Specifies the timeout for the selected route. Timeout is computed from the time + the request is has been fully processed (i.e. end-of-stream) up until the + response has been completely processed. Timeout includes all retries. If not + specified, the default value is 15 seconds. Structure is documented below. + +* `url_rewrite` - + (Optional) + The spec to modify the URL of the request, prior to forwarding the request to + the matched service Structure is documented below. + +* `weighted_backend_services` - + (Optional) + A list of weighted backend services to send traffic to when a route match + occurs. The weights determine the fraction of traffic that flows to their + corresponding backend service. If all traffic needs to go to a single backend + service, there must be one weightedBackendService with weight set to a non 0 + number. Once a backendService is identified and before forwarding the request to + the backend service, advanced routing actions like Url rewrites and header + transformations are applied depending on additional settings specified in this + HttpRouteAction. Structure is documented below. + + +The `cors_policy` block supports: + +* `allow_credentials` - + (Optional) + In response to a preflight request, setting this to true indicates that the + actual request can include user credentials. This translates to the Access- + Control-Allow-Credentials header. Defaults to false. + +* `allow_headers` - + (Optional) + Specifies the content for the Access-Control-Allow-Headers header. + +* `allow_methods` - + (Optional) + Specifies the content for the Access-Control-Allow-Methods header. + +* `allow_origin_regexes` - + (Optional) + Specifies the regualar expression patterns that match allowed origins. For + regular expression grammar please see en.cppreference.com/w/cpp/regex/ecmascript + An origin is allowed if it matches either allow_origins or allow_origin_regex. + +* `allow_origins` - + (Optional) + Specifies the list of origins that will be allowed to do CORS requests. An + origin is allowed if it matches either allow_origins or allow_origin_regex. + +* `disabled` - + (Optional) + If true, specifies the CORS policy is disabled. + which indicates that the CORS policy is in effect. Defaults to false. + +* `expose_headers` - + (Optional) + Specifies the content for the Access-Control-Expose-Headers header. + +* `max_age` - + (Optional) + Specifies how long the results of a preflight request can be cached. This + translates to the content for the Access-Control-Max-Age header. + +The `fault_injection_policy` block supports: + +* `abort` - + (Optional) + The specification for how client requests are aborted as part of fault + injection. Structure is documented below. + +* `delay` - + (Optional) + The specification for how client requests are delayed as part of fault + injection, before being sent to a backend service. Structure is documented below. + + +The `abort` block supports: + +* `http_status` - + (Optional) + The HTTP status code used to abort the request. The value must be between 200 + and 599 inclusive. + +* `percentage` - + (Optional) + The percentage of traffic (connections/operations/requests) which will be + aborted as part of fault injection. The value must be between 0.0 and 100.0 + inclusive. + +The `delay` block supports: + +* `fixed_delay` - + (Optional) + Specifies the value of the fixed delay interval. Structure is documented below. + +* `percentage` - + (Optional) + The percentage of traffic (connections/operations/requests) on which delay will + be introduced as part of fault injection. The value must be between 0.0 and + 100.0 inclusive. + + +The `fixed_delay` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `request_mirror_policy` block supports: + +* `backend_service` - + (Required) + The RegionBackendService resource being mirrored to. + +The `retry_policy` block supports: + +* `num_retries` - + (Required) + Specifies the allowed number retries. This number must be > 0. + +* `per_try_timeout` - + (Optional) + Specifies a non-zero timeout per retry attempt. Structure is documented below. + +* `retry_conditions` - + (Optional) + Specfies one or more conditions when this retry rule applies. Valid values are: + - 5xx: Loadbalancer will attempt a retry if the backend service responds with + any 5xx response code, or if the backend service does not respond at all, + example: disconnects, reset, read timeout, connection failure, and refused + streams. + - gateway-error: Similar to 5xx, but only applies to response codes + 502, 503 or 504. + - connect-failure: Loadbalancer will retry on failures + connecting to backend services, for example due to connection timeouts. + - retriable-4xx: Loadbalancer will retry for retriable 4xx response codes. + Currently the only retriable error supported is 409. + - refused-stream: Loadbalancer will retry if the backend service resets the stream with a + REFUSED_STREAM error code. This reset type indicates that it is safe to retry. + - cancelled: Loadbalancer will retry if the gRPC status code in the response + header is set to cancelled + - deadline-exceeded: Loadbalancer will retry if the + gRPC status code in the response header is set to deadline-exceeded + - resource-exhausted: Loadbalancer will retry if the gRPC status code in the response + header is set to resource-exhausted + - unavailable: Loadbalancer will retry if the gRPC status code in + the response header is set to unavailable + + +The `per_try_timeout` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `timeout` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `url_rewrite` block supports: + +* `host_rewrite` - + (Optional) + Prior to forwarding the request to the selected service, the request's host + header is replaced with contents of hostRewrite. The value must be between 1 and + 255 characters. + +* `path_prefix_rewrite` - + (Optional) + Prior to forwarding the request to the selected backend service, the matching + portion of the request's path is replaced by pathPrefixRewrite. The value must + be between 1 and 1024 characters. + +The `weighted_backend_services` block supports: + +* `backend_service` - + (Required) + The default RegionBackendService resource. Before + forwarding the request to backendService, the loadbalancer applies any relevant + headerActions specified as part of this backendServiceWeight. + +* `header_action` - + (Optional) + Specifies changes to request and response headers that need to take effect for + the selected backendService. headerAction specified here take effect before + headerAction in the enclosing HttpRouteRule, PathMatcher and UrlMap. Structure is documented below. + +* `weight` - + (Required) + Specifies the fraction of traffic sent to backendService, computed as weight / + (sum of all weightedBackendService weights in routeAction) . The selection of a + backend service is determined only for new traffic. Once a user's request has + been directed to a backendService, subsequent requests will be sent to the same + backendService as determined by the BackendService's session affinity policy. + The value must be between 0 and 1000 + + +The `header_action` block supports: + +* `request_headers_to_add` - + (Optional) + Headers to add to a matching request prior to forwarding the request to the + backendService. Structure is documented below. + +* `request_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the request + prior to forwarding the request to the backendService. + +* `response_headers_to_add` - + (Optional) + Headers to add the response prior to sending the response back to the client. Structure is documented below. + +* `response_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the response + prior to sending the response back to the client. + + +The `request_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `response_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `url_redirect` block supports: + +* `host_redirect` - + (Optional) + The host that will be used in the redirect response instead of the one + that was supplied in the request. The value must be between 1 and 255 + characters. + +* `https_redirect` - + (Optional) + If set to true, the URL scheme in the redirected request is set to https. + If set to false, the URL scheme of the redirected request will remain the + same as that of the request. This must only be set for UrlMaps used in + TargetHttpProxys. Setting this true for TargetHttpsProxy is not + permitted. The default is set to false. + +* `path_redirect` - + (Optional) + The path that will be used in the redirect response instead of the one + that was supplied in the request. pathRedirect cannot be supplied + together with prefixRedirect. Supply one alone or neither. If neither is + supplied, the path of the original request will be used for the redirect. + The value must be between 1 and 1024 characters. + +* `prefix_redirect` - + (Optional) + The prefix that replaces the prefixMatch specified in the + HttpRouteRuleMatch, retaining the remaining portion of the URL before + redirecting the request. prefixRedirect cannot be supplied together with + pathRedirect. Supply one alone or neither. If neither is supplied, the + path of the original request will be used for the redirect. The value + must be between 1 and 1024 characters. + +* `redirect_response_code` - + (Optional) + The HTTP Status code to use for this RedirectAction. Supported values are: + - MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. + - FOUND, which corresponds to 302. + - SEE_OTHER which corresponds to 303. + - TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method + will be retained. + - PERMANENT_REDIRECT, which corresponds to 308. In this case, + the request method will be retained. + + Possible values are: + * `FOUND` + * `MOVED_PERMANENTLY_DEFAULT` + * `PERMANENT_REDIRECT` + * `SEE_OTHER` + * `TEMPORARY_REDIRECT` + +* `strip_query` - + (Optional) + If set to true, any accompanying query portion of the original URL is + removed prior to redirecting the request. If set to false, the query + portion of the original URL is retained. The default is set to false. + +The `path_rule` block supports: + +* `service` - + (Optional) + The region backend service resource to which traffic is + directed if this rule is matched. If routeAction is additionally specified, + advanced routing actions like URL Rewrites, etc. take effect prior to sending + the request to the backend. However, if service is specified, routeAction cannot + contain any weightedBackendService s. Conversely, if routeAction specifies any + weightedBackendServices, service must not be specified. Only one of urlRedirect, + service or routeAction.weightedBackendService must be set. + +* `paths` - + (Required) + The list of path patterns to match. Each must start with / and the only place a + * is allowed is at the end following a /. The string fed to the path matcher + does not include any text after the first ? or #, and those chars are not + allowed here. + +* `route_action` - + (Optional) + In response to a matching path, the load balancer performs advanced routing + actions like URL rewrites, header transformations, etc. prior to forwarding the + request to the selected backend. If routeAction specifies any + weightedBackendServices, service must not be set. Conversely if service is set, + routeAction cannot contain any weightedBackendServices. Only one of routeAction + or urlRedirect must be set. Structure is documented below. + +* `url_redirect` - + (Optional) + When a path pattern is matched, the request is redirected to a URL specified + by urlRedirect. If urlRedirect is specified, service or routeAction must not + be set. Structure is documented below. + + +The `route_action` block supports: + +* `cors_policy` - + (Optional) + The specification for allowing client side cross-origin requests. Please see W3C + Recommendation for Cross Origin Resource Sharing Structure is documented below. + +* `fault_injection_policy` - + (Optional) + The specification for fault injection introduced into traffic to test the + resiliency of clients to backend service failure. As part of fault injection, + when clients send requests to a backend service, delays can be introduced by + Loadbalancer on a percentage of requests before sending those request to the + backend service. Similarly requests from clients can be aborted by the + Loadbalancer for a percentage of requests. timeout and retry_policy will be + ignored by clients that are configured with a fault_injection_policy. Structure is documented below. + +* `request_mirror_policy` - + (Optional) + Specifies the policy on how requests intended for the route's backends are + shadowed to a separate mirrored backend service. Loadbalancer does not wait for + responses from the shadow service. Prior to sending traffic to the shadow + service, the host / authority header is suffixed with -shadow. Structure is documented below. + +* `retry_policy` - + (Optional) + Specifies the retry policy associated with this route. Structure is documented below. + +* `timeout` - + (Optional) + Specifies the timeout for the selected route. Timeout is computed from the time + the request is has been fully processed (i.e. end-of-stream) up until the + response has been completely processed. Timeout includes all retries. If not + specified, the default value is 15 seconds. Structure is documented below. + +* `url_rewrite` - + (Optional) + The spec to modify the URL of the request, prior to forwarding the request to + the matched service Structure is documented below. + +* `weighted_backend_services` - + (Optional) + A list of weighted backend services to send traffic to when a route match + occurs. The weights determine the fraction of traffic that flows to their + corresponding backend service. If all traffic needs to go to a single backend + service, there must be one weightedBackendService with weight set to a non 0 + number. Once a backendService is identified and before forwarding the request to + the backend service, advanced routing actions like Url rewrites and header + transformations are applied depending on additional settings specified in this + HttpRouteAction. Structure is documented below. + + +The `cors_policy` block supports: + +* `allow_credentials` - + (Optional) + In response to a preflight request, setting this to true indicates that the + actual request can include user credentials. This translates to the Access- + Control-Allow-Credentials header. Defaults to false. + +* `allow_headers` - + (Optional) + Specifies the content for the Access-Control-Allow-Headers header. + +* `allow_methods` - + (Optional) + Specifies the content for the Access-Control-Allow-Methods header. + +* `allow_origin_regexes` - + (Optional) + Specifies the regualar expression patterns that match allowed origins. For + regular expression grammar please see en.cppreference.com/w/cpp/regex/ecmascript + An origin is allowed if it matches either allow_origins or allow_origin_regex. + +* `allow_origins` - + (Optional) + Specifies the list of origins that will be allowed to do CORS requests. An + origin is allowed if it matches either allow_origins or allow_origin_regex. + +* `disabled` - + (Required) + If true, specifies the CORS policy is disabled. + +* `expose_headers` - + (Optional) + Specifies the content for the Access-Control-Expose-Headers header. + +* `max_age` - + (Optional) + Specifies how long the results of a preflight request can be cached. This + translates to the content for the Access-Control-Max-Age header. + +The `fault_injection_policy` block supports: + +* `abort` - + (Optional) + The specification for how client requests are aborted as part of fault + injection. Structure is documented below. + +* `delay` - + (Optional) + The specification for how client requests are delayed as part of fault + injection, before being sent to a backend service. Structure is documented below. + + +The `abort` block supports: + +* `http_status` - + (Required) + The HTTP status code used to abort the request. The value must be between 200 + and 599 inclusive. + +* `percentage` - + (Required) + The percentage of traffic (connections/operations/requests) which will be + aborted as part of fault injection. The value must be between 0.0 and 100.0 + inclusive. + +The `delay` block supports: + +* `fixed_delay` - + (Required) + Specifies the value of the fixed delay interval. Structure is documented below. + +* `percentage` - + (Required) + The percentage of traffic (connections/operations/requests) on which delay will + be introduced as part of fault injection. The value must be between 0.0 and + 100.0 inclusive. + + +The `fixed_delay` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `request_mirror_policy` block supports: + +* `backend_service` - + (Required) + The RegionBackendService resource being mirrored to. + +The `retry_policy` block supports: + +* `num_retries` - + (Optional) + Specifies the allowed number retries. This number must be > 0. + +* `per_try_timeout` - + (Optional) + Specifies a non-zero timeout per retry attempt. Structure is documented below. + +* `retry_conditions` - + (Optional) + Specifies one or more conditions when this retry rule applies. Valid values are: + - 5xx: Loadbalancer will attempt a retry if the backend service responds with + any 5xx response code, or if the backend service does not respond at all, + example: disconnects, reset, read timeout, connection failure, and refused + streams. + - gateway-error: Similar to 5xx, but only applies to response codes + 502, 503 or 504. + - connect-failure: Loadbalancer will retry on failures + connecting to backend services, for example due to connection timeouts. + - retriable-4xx: Loadbalancer will retry for retriable 4xx response codes. + Currently the only retriable error supported is 409. + - refused-stream: Loadbalancer will retry if the backend service resets the stream with a + REFUSED_STREAM error code. This reset type indicates that it is safe to retry. + - cancelled: Loadbalancer will retry if the gRPC status code in the response + header is set to cancelled + - deadline-exceeded: Loadbalancer will retry if the + gRPC status code in the response header is set to deadline-exceeded + - resource-exhausted: Loadbalancer will retry if the gRPC status code in the response + header is set to resource-exhausted + - unavailable: Loadbalancer will retry if + the gRPC status code in the response header is set to unavailable + + +The `per_try_timeout` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `timeout` block supports: + +* `nanos` - + (Optional) + Span of time that's a fraction of a second at nanosecond resolution. Durations + less than one second are represented with a 0 `seconds` field and a positive + `nanos` field. Must be from 0 to 999,999,999 inclusive. + +* `seconds` - + (Required) + Span of time at a resolution of a second. Must be from 0 to 315,576,000,000 + inclusive. + +The `url_rewrite` block supports: + +* `host_rewrite` - + (Optional) + Prior to forwarding the request to the selected service, the request's host + header is replaced with contents of hostRewrite. The value must be between 1 and + 255 characters. + +* `path_prefix_rewrite` - + (Optional) + Prior to forwarding the request to the selected backend service, the matching + portion of the request's path is replaced by pathPrefixRewrite. The value must + be between 1 and 1024 characters. + +The `weighted_backend_services` block supports: + +* `backend_service` - + (Required) + The default RegionBackendService resource. Before + forwarding the request to backendService, the loadbalancer applies any relevant + headerActions specified as part of this backendServiceWeight. + +* `header_action` - + (Optional) + Specifies changes to request and response headers that need to take effect for + the selected backendService. headerAction specified here take effect before + headerAction in the enclosing HttpRouteRule, PathMatcher and UrlMap. Structure is documented below. + +* `weight` - + (Required) + Specifies the fraction of traffic sent to backendService, computed as weight / + (sum of all weightedBackendService weights in routeAction) . The selection of a + backend service is determined only for new traffic. Once a user's request has + been directed to a backendService, subsequent requests will be sent to the same + backendService as determined by the BackendService's session affinity policy. + The value must be between 0 and 1000 + + +The `header_action` block supports: + +* `request_headers_to_add` - + (Optional) + Headers to add to a matching request prior to forwarding the request to the + backendService. Structure is documented below. + +* `request_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the request + prior to forwarding the request to the backendService. + +* `response_headers_to_add` - + (Optional) + Headers to add the response prior to sending the response back to the client. Structure is documented below. + +* `response_headers_to_remove` - + (Optional) + A list of header names for headers that need to be removed from the response + prior to sending the response back to the client. + + +The `request_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `response_headers_to_add` block supports: + +* `header_name` - + (Required) + The name of the header. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Required) + If false, headerValue is appended to any values that already exist for the + header. If true, headerValue is set for the header, discarding any values that + were set for that header. + +The `url_redirect` block supports: + +* `host_redirect` - + (Optional) + The host that will be used in the redirect response instead of the one + that was supplied in the request. The value must be between 1 and 255 + characters. + +* `https_redirect` - + (Optional) + If set to true, the URL scheme in the redirected request is set to https. + If set to false, the URL scheme of the redirected request will remain the + same as that of the request. This must only be set for UrlMaps used in + TargetHttpProxys. Setting this true for TargetHttpsProxy is not + permitted. The default is set to false. + +* `path_redirect` - + (Optional) + The path that will be used in the redirect response instead of the one + that was supplied in the request. pathRedirect cannot be supplied + together with prefixRedirect. Supply one alone or neither. If neither is + supplied, the path of the original request will be used for the redirect. + The value must be between 1 and 1024 characters. + +* `prefix_redirect` - + (Optional) + The prefix that replaces the prefixMatch specified in the + HttpRouteRuleMatch, retaining the remaining portion of the URL before + redirecting the request. prefixRedirect cannot be supplied together with + pathRedirect. Supply one alone or neither. If neither is supplied, the + path of the original request will be used for the redirect. The value + must be between 1 and 1024 characters. + +* `redirect_response_code` - + (Optional) + The HTTP Status code to use for this RedirectAction. Supported values are: + - MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. + - FOUND, which corresponds to 302. + - SEE_OTHER which corresponds to 303. + - TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method + will be retained. + - PERMANENT_REDIRECT, which corresponds to 308. In this case, + the request method will be retained. + + Possible values are: + * `FOUND` + * `MOVED_PERMANENTLY_DEFAULT` + * `PERMANENT_REDIRECT` + * `SEE_OTHER` + * `TEMPORARY_REDIRECT` + +* `strip_query` - + (Optional) + If set to true, any accompanying query portion of the original URL is removed + prior to redirecting the request. If set to false, the query portion of the + original URL is retained. + +The `default_url_redirect` block supports: + +* `host_redirect` - + (Optional) + The host that will be used in the redirect response instead of the one that was + supplied in the request. The value must be between 1 and 255 characters. + +* `https_redirect` - + (Optional) + If set to true, the URL scheme in the redirected request is set to https. If set to + false, the URL scheme of the redirected request will remain the same as that of the + request. This must only be set for UrlMaps used in TargetHttpProxys. Setting this + true for TargetHttpsProxy is not permitted. The default is set to false. + +* `path_redirect` - + (Optional) + The path that will be used in the redirect response instead of the one that was + supplied in the request. pathRedirect cannot be supplied together with + prefixRedirect. Supply one alone or neither. If neither is supplied, the path of the + original request will be used for the redirect. The value must be between 1 and 1024 + characters. + +* `prefix_redirect` - + (Optional) + The prefix that replaces the prefixMatch specified in the HttpRouteRuleMatch, + retaining the remaining portion of the URL before redirecting the request. + prefixRedirect cannot be supplied together with pathRedirect. Supply one alone or + neither. If neither is supplied, the path of the original request will be used for + the redirect. The value must be between 1 and 1024 characters. + +* `redirect_response_code` - + (Optional) + The HTTP Status code to use for this RedirectAction. Supported values are: + - MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. + - FOUND, which corresponds to 302. + - SEE_OTHER which corresponds to 303. + - TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method + will be retained. + - PERMANENT_REDIRECT, which corresponds to 308. In this case, + the request method will be retained. + + Possible values are: + * `FOUND` + * `MOVED_PERMANENTLY_DEFAULT` + * `PERMANENT_REDIRECT` + * `SEE_OTHER` + * `TEMPORARY_REDIRECT` + +* `strip_query` - + (Optional) + If set to true, any accompanying query portion of the original URL is removed prior + to redirecting the request. If set to false, the query portion of the original URL is + retained. The default is set to false. + +The `test` block supports: + +* `description` - + (Optional) + Description of this test case. + +* `host` - + (Required) + Host portion of the URL. + +* `path` - + (Required) + Path portion of the URL. + +* `service` - + (Required) + A reference to expected RegionBackendService resource the given URL should be mapped to. + +The `default_url_redirect` block supports: + +* `host_redirect` - + (Optional) + The host that will be used in the redirect response instead of the one that was + supplied in the request. The value must be between 1 and 255 characters. + +* `https_redirect` - + (Optional) + If set to true, the URL scheme in the redirected request is set to https. If set to + false, the URL scheme of the redirected request will remain the same as that of the + request. This must only be set for UrlMaps used in TargetHttpProxys. Setting this + true for TargetHttpsProxy is not permitted. The default is set to false. + +* `path_redirect` - + (Optional) + The path that will be used in the redirect response instead of the one that was + supplied in the request. pathRedirect cannot be supplied together with + prefixRedirect. Supply one alone or neither. If neither is supplied, the path of the + original request will be used for the redirect. The value must be between 1 and 1024 + characters. + +* `prefix_redirect` - + (Optional) + The prefix that replaces the prefixMatch specified in the HttpRouteRuleMatch, + retaining the remaining portion of the URL before redirecting the request. + prefixRedirect cannot be supplied together with pathRedirect. Supply one alone or + neither. If neither is supplied, the path of the original request will be used for + the redirect. The value must be between 1 and 1024 characters. + +* `redirect_response_code` - + (Optional) + The HTTP Status code to use for this RedirectAction. Supported values are: + - MOVED_PERMANENTLY_DEFAULT, which is the default value and corresponds to 301. + - FOUND, which corresponds to 302. + - SEE_OTHER which corresponds to 303. + - TEMPORARY_REDIRECT, which corresponds to 307. In this case, the request method + will be retained. + - PERMANENT_REDIRECT, which corresponds to 308. In this case, + the request method will be retained. + + Possible values are: + * `FOUND` + * `MOVED_PERMANENTLY_DEFAULT` + * `PERMANENT_REDIRECT` + * `SEE_OTHER` + * `TEMPORARY_REDIRECT` + +* `strip_query` - + (Optional) + If set to true, any accompanying query portion of the original URL is removed prior + to redirecting the request. If set to false, the query portion of the original URL is + retained. The default is set to false. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/regions/{{region}}/urlMaps/{{name}}` + +* `creation_timestamp` - + Creation timestamp in RFC3339 text format. + +* `map_id` - + The unique identifier for the resource. + +* `fingerprint` - + Fingerprint of this resource. This field is used internally during + updates of this resource. +* `self_link` - The URI of the created resource. + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 4 minutes. +- `update` - Default is 4 minutes. +- `delete` - Default is 4 minutes. + +## Import + +RegionUrlMap can be imported using any of these accepted formats: + +``` +$ terraform import google_compute_region_url_map.default projects/{{project}}/regions/{{region}}/urlMaps/{{name}} +$ terraform import google_compute_region_url_map.default {{project}}/{{region}}/{{name}} +$ terraform import google_compute_region_url_map.default {{region}}/{{name}} +$ terraform import google_compute_region_url_map.default {{name}} +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/google.erb b/website/google.erb index 6d209d9cd73..f833801a587 100644 --- a/website/google.erb +++ b/website/google.erb @@ -1325,6 +1325,18 @@ google_compute_region_ssl_certificate +
  • + google_compute_region_target_http_proxy +
  • + +
  • + google_compute_region_target_https_proxy +
  • + +
  • + google_compute_region_url_map +
  • +
  • google_compute_reservation