From a60eda578876a315e798d04f4d60a0f294adcccc Mon Sep 17 00:00:00 2001 From: The Magician Date: Fri, 9 Jul 2021 12:23:26 -0500 Subject: [PATCH] port media edge resources over from EAP provider (#4958) (#9540) Signed-off-by: Modular Magician --- .changelog/4958.txt | 7 + google/certificate_manager_operation.go | 61 + google/config.go | 8 + google/network_services_operation.go | 61 + google/provider.go | 27 +- ...esource_certificate_manager_certificate.go | 595 ++++ ...cate_manager_certificate_generated_test.go | 115 + ...ficate_manager_certificate_sweeper_test.go | 124 + ...e_certificate_manager_dns_authorization.go | 430 +++ ...anager_dns_authorization_generated_test.go | 104 + ..._manager_dns_authorization_sweeper_test.go | 124 + ...urce_network_services_edge_cache_keyset.go | 459 +++ ...rvices_edge_cache_keyset_generated_test.go | 100 + ...services_edge_cache_keyset_sweeper_test.go | 124 + ...urce_network_services_edge_cache_origin.go | 710 ++++ ...rvices_edge_cache_origin_generated_test.go | 157 + ...services_edge_cache_origin_sweeper_test.go | 124 + ...network_services_edge_cache_origin_test.go | 67 + ...rce_network_services_edge_cache_service.go | 2972 +++++++++++++++++ ...vices_edge_cache_service_generated_test.go | 348 ++ ...ervices_edge_cache_service_sweeper_test.go | 124 + ...etwork_services_edge_cache_service_test.go | 140 + ...context_manager_access_level.html.markdown | 6 +- ...nager_access_level_condition.html.markdown | 6 +- ...ontext_manager_access_policy.html.markdown | 6 +- ...r_service_perimeter_resource.html.markdown | 6 +- ...tificate_manager_certificate.html.markdown | 181 + ...te_manager_dns_authorization.html.markdown | 138 + ...k_services_edge_cache_keyset.html.markdown | 134 + ...k_services_edge_cache_origin.html.markdown | 220 ++ ..._services_edge_cache_service.html.markdown | 847 +++++ website/google.erb | 44 + 32 files changed, 8555 insertions(+), 14 deletions(-) create mode 100644 .changelog/4958.txt create mode 100644 google/certificate_manager_operation.go create mode 100644 google/network_services_operation.go create mode 100644 google/resource_certificate_manager_certificate.go create mode 100644 google/resource_certificate_manager_certificate_generated_test.go create mode 100644 google/resource_certificate_manager_certificate_sweeper_test.go create mode 100644 google/resource_certificate_manager_dns_authorization.go create mode 100644 google/resource_certificate_manager_dns_authorization_generated_test.go create mode 100644 google/resource_certificate_manager_dns_authorization_sweeper_test.go create mode 100644 google/resource_network_services_edge_cache_keyset.go create mode 100644 google/resource_network_services_edge_cache_keyset_generated_test.go create mode 100644 google/resource_network_services_edge_cache_keyset_sweeper_test.go create mode 100644 google/resource_network_services_edge_cache_origin.go create mode 100644 google/resource_network_services_edge_cache_origin_generated_test.go create mode 100644 google/resource_network_services_edge_cache_origin_sweeper_test.go create mode 100644 google/resource_network_services_edge_cache_origin_test.go create mode 100644 google/resource_network_services_edge_cache_service.go create mode 100644 google/resource_network_services_edge_cache_service_generated_test.go create mode 100644 google/resource_network_services_edge_cache_service_sweeper_test.go create mode 100644 google/resource_network_services_edge_cache_service_test.go create mode 100644 website/docs/r/certificate_manager_certificate.html.markdown create mode 100644 website/docs/r/certificate_manager_dns_authorization.html.markdown create mode 100644 website/docs/r/network_services_edge_cache_keyset.html.markdown create mode 100644 website/docs/r/network_services_edge_cache_origin.html.markdown create mode 100644 website/docs/r/network_services_edge_cache_service.html.markdown diff --git a/.changelog/4958.txt b/.changelog/4958.txt new file mode 100644 index 00000000000..296275aaa70 --- /dev/null +++ b/.changelog/4958.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +`google_certificate_manager_certificate` +`google_certificate_manager_dns_authorization` +`google_network_services_edge_cache_keyset` +`google_network_services_edge_cache_origin` +`google_network_services_edge_cache_service` +``` diff --git a/google/certificate_manager_operation.go b/google/certificate_manager_operation.go new file mode 100644 index 00000000000..7544761e2f4 --- /dev/null +++ b/google/certificate_manager_operation.go @@ -0,0 +1,61 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "time" +) + +type CertificateManagerOperationWaiter struct { + Config *Config + UserAgent string + Project string + CommonOperationWaiter +} + +func (w *CertificateManagerOperationWaiter) QueryOp() (interface{}, error) { + if w == nil { + return nil, fmt.Errorf("Cannot query operation, it's unset or nil.") + } + // Returns the proper get. + url := fmt.Sprintf("https://certificatemanager.googleapis.com/v1alpha2/%s", w.CommonOperationWaiter.Op.Name) + + return sendRequest(w.Config, "GET", w.Project, url, w.UserAgent, nil) +} + +func createCertificateManagerWaiter(config *Config, op map[string]interface{}, project, activity, userAgent string) (*CertificateManagerOperationWaiter, error) { + w := &CertificateManagerOperationWaiter{ + Config: config, + UserAgent: userAgent, + Project: project, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +func certificateManagerOperationWaitTime(config *Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + if val, ok := op["name"]; !ok || val == "" { + // This was a synchronous call - there is no operation to wait for. + return nil + } + w, err := createCertificateManagerWaiter(config, op, project, activity, userAgent) + if err != nil { + // If w is nil, the op was synchronous. + return err + } + return OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google/config.go b/google/config.go index f4c3a3d0149..6c2eff9baa0 100644 --- a/google/config.go +++ b/google/config.go @@ -91,6 +91,7 @@ type Config struct { BigtableBasePath string BillingBasePath string BinaryAuthorizationBasePath string + CertificateManagerBasePath string CloudAssetBasePath string CloudBuildBasePath string CloudFunctionsBasePath string @@ -121,6 +122,7 @@ type Config struct { MLEngineBasePath string MonitoringBasePath string NetworkManagementBasePath string + NetworkServicesBasePath string NotebooksBasePath string OSConfigBasePath string OSLoginBasePath string @@ -180,6 +182,7 @@ const BigqueryReservationBasePathKey = "BigqueryReservation" const BigtableBasePathKey = "Bigtable" const BillingBasePathKey = "Billing" const BinaryAuthorizationBasePathKey = "BinaryAuthorization" +const CertificateManagerBasePathKey = "CertificateManager" const CloudAssetBasePathKey = "CloudAsset" const CloudBuildBasePathKey = "CloudBuild" const CloudFunctionsBasePathKey = "CloudFunctions" @@ -210,6 +213,7 @@ const MemcacheBasePathKey = "Memcache" const MLEngineBasePathKey = "MLEngine" const MonitoringBasePathKey = "Monitoring" const NetworkManagementBasePathKey = "NetworkManagement" +const NetworkServicesBasePathKey = "NetworkServices" const NotebooksBasePathKey = "Notebooks" const OSConfigBasePathKey = "OSConfig" const OSLoginBasePathKey = "OSLogin" @@ -260,6 +264,7 @@ var DefaultBasePaths = map[string]string{ BigtableBasePathKey: "https://bigtableadmin.googleapis.com/v2/", BillingBasePathKey: "https://billingbudgets.googleapis.com/v1/", BinaryAuthorizationBasePathKey: "https://binaryauthorization.googleapis.com/v1/", + CertificateManagerBasePathKey: "https://certificatemanager.googleapis.com/v1alpha2/", CloudAssetBasePathKey: "https://cloudasset.googleapis.com/v1/", CloudBuildBasePathKey: "https://cloudbuild.googleapis.com/v1/", CloudFunctionsBasePathKey: "https://cloudfunctions.googleapis.com/v1/", @@ -290,6 +295,7 @@ var DefaultBasePaths = map[string]string{ MLEngineBasePathKey: "https://ml.googleapis.com/v1/", MonitoringBasePathKey: "https://monitoring.googleapis.com/", NetworkManagementBasePathKey: "https://networkmanagement.googleapis.com/v1/", + NetworkServicesBasePathKey: "https://networkservices.googleapis.com/v1/", NotebooksBasePathKey: "https://notebooks.googleapis.com/v1/", OSConfigBasePathKey: "https://osconfig.googleapis.com/v1/", OSLoginBasePathKey: "https://oslogin.googleapis.com/v1/", @@ -1093,6 +1099,7 @@ func ConfigureBasePaths(c *Config) { c.BigtableBasePath = DefaultBasePaths[BigtableBasePathKey] c.BillingBasePath = DefaultBasePaths[BillingBasePathKey] c.BinaryAuthorizationBasePath = DefaultBasePaths[BinaryAuthorizationBasePathKey] + c.CertificateManagerBasePath = DefaultBasePaths[CertificateManagerBasePathKey] c.CloudAssetBasePath = DefaultBasePaths[CloudAssetBasePathKey] c.CloudBuildBasePath = DefaultBasePaths[CloudBuildBasePathKey] c.CloudFunctionsBasePath = DefaultBasePaths[CloudFunctionsBasePathKey] @@ -1123,6 +1130,7 @@ func ConfigureBasePaths(c *Config) { c.MLEngineBasePath = DefaultBasePaths[MLEngineBasePathKey] c.MonitoringBasePath = DefaultBasePaths[MonitoringBasePathKey] c.NetworkManagementBasePath = DefaultBasePaths[NetworkManagementBasePathKey] + c.NetworkServicesBasePath = DefaultBasePaths[NetworkServicesBasePathKey] c.NotebooksBasePath = DefaultBasePaths[NotebooksBasePathKey] c.OSConfigBasePath = DefaultBasePaths[OSConfigBasePathKey] c.OSLoginBasePath = DefaultBasePaths[OSLoginBasePathKey] diff --git a/google/network_services_operation.go b/google/network_services_operation.go new file mode 100644 index 00000000000..8a761d35b86 --- /dev/null +++ b/google/network_services_operation.go @@ -0,0 +1,61 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "time" +) + +type NetworkServicesOperationWaiter struct { + Config *Config + UserAgent string + Project string + CommonOperationWaiter +} + +func (w *NetworkServicesOperationWaiter) QueryOp() (interface{}, error) { + if w == nil { + return nil, fmt.Errorf("Cannot query operation, it's unset or nil.") + } + // Returns the proper get. + url := fmt.Sprintf("https://networkservices.googleapis.com/v1/%s", w.CommonOperationWaiter.Op.Name) + + return sendRequest(w.Config, "GET", w.Project, url, w.UserAgent, nil) +} + +func createNetworkServicesWaiter(config *Config, op map[string]interface{}, project, activity, userAgent string) (*NetworkServicesOperationWaiter, error) { + w := &NetworkServicesOperationWaiter{ + Config: config, + UserAgent: userAgent, + Project: project, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +func networkServicesOperationWaitTime(config *Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + if val, ok := op["name"]; !ok || val == "" { + // This was a synchronous call - there is no operation to wait for. + return nil + } + w, err := createNetworkServicesWaiter(config, op, project, activity, userAgent) + if err != nil { + // If w is nil, the op was synchronous. + return err + } + return OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google/provider.go b/google/provider.go index 6f44ad49697..37b355004f2 100644 --- a/google/provider.go +++ b/google/provider.go @@ -227,6 +227,14 @@ func Provider() *schema.Provider { "GOOGLE_BINARY_AUTHORIZATION_CUSTOM_ENDPOINT", }, DefaultBasePaths[BinaryAuthorizationBasePathKey]), }, + "certificate_manager_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CERTIFICATE_MANAGER_CUSTOM_ENDPOINT", + }, DefaultBasePaths[CertificateManagerBasePathKey]), + }, "cloud_asset_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -467,6 +475,14 @@ func Provider() *schema.Provider { "GOOGLE_NETWORK_MANAGEMENT_CUSTOM_ENDPOINT", }, DefaultBasePaths[NetworkManagementBasePathKey]), }, + "network_services_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_NETWORK_SERVICES_CUSTOM_ENDPOINT", + }, DefaultBasePaths[NetworkServicesBasePathKey]), + }, "notebooks_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -775,9 +791,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 202 +// Generated resources: 207 // Generated IAM resources: 90 -// Total generated resources: 292 +// Total generated resources: 297 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -828,6 +844,8 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_binary_authorization_attestor_iam_member": ResourceIamMember(BinaryAuthorizationAttestorIamSchema, BinaryAuthorizationAttestorIamUpdaterProducer, BinaryAuthorizationAttestorIdParseFunc), "google_binary_authorization_attestor_iam_policy": ResourceIamPolicy(BinaryAuthorizationAttestorIamSchema, BinaryAuthorizationAttestorIamUpdaterProducer, BinaryAuthorizationAttestorIdParseFunc), "google_binary_authorization_policy": resourceBinaryAuthorizationPolicy(), + "google_certificate_manager_dns_authorization": resourceCertificateManagerDnsAuthorization(), + "google_certificate_manager_certificate": resourceCertificateManagerCertificate(), "google_cloud_asset_project_feed": resourceCloudAssetProjectFeed(), "google_cloud_asset_folder_feed": resourceCloudAssetFolderFeed(), "google_cloud_asset_organization_feed": resourceCloudAssetOrganizationFeed(), @@ -1014,6 +1032,9 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_monitoring_uptime_check_config": resourceMonitoringUptimeCheckConfig(), "google_monitoring_metric_descriptor": resourceMonitoringMetricDescriptor(), "google_network_management_connectivity_test": resourceNetworkManagementConnectivityTest(), + "google_network_services_edge_cache_keyset": resourceNetworkServicesEdgeCacheKeyset(), + "google_network_services_edge_cache_origin": resourceNetworkServicesEdgeCacheOrigin(), + "google_network_services_edge_cache_service": resourceNetworkServicesEdgeCacheService(), "google_notebooks_environment": resourceNotebooksEnvironment(), "google_notebooks_instance": resourceNotebooksInstance(), "google_notebooks_instance_iam_binding": ResourceIamBinding(NotebooksInstanceIamSchema, NotebooksInstanceIamUpdaterProducer, NotebooksInstanceIdParseFunc), @@ -1297,6 +1318,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.BigtableBasePath = d.Get("bigtable_custom_endpoint").(string) config.BillingBasePath = d.Get("billing_custom_endpoint").(string) config.BinaryAuthorizationBasePath = d.Get("binary_authorization_custom_endpoint").(string) + config.CertificateManagerBasePath = d.Get("certificate_manager_custom_endpoint").(string) config.CloudAssetBasePath = d.Get("cloud_asset_custom_endpoint").(string) config.CloudBuildBasePath = d.Get("cloud_build_custom_endpoint").(string) config.CloudFunctionsBasePath = d.Get("cloud_functions_custom_endpoint").(string) @@ -1327,6 +1349,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.MLEngineBasePath = d.Get("ml_engine_custom_endpoint").(string) config.MonitoringBasePath = d.Get("monitoring_custom_endpoint").(string) config.NetworkManagementBasePath = d.Get("network_management_custom_endpoint").(string) + config.NetworkServicesBasePath = d.Get("network_services_custom_endpoint").(string) config.NotebooksBasePath = d.Get("notebooks_custom_endpoint").(string) config.OSConfigBasePath = d.Get("os_config_custom_endpoint").(string) config.OSLoginBasePath = d.Get("os_login_custom_endpoint").(string) diff --git a/google/resource_certificate_manager_certificate.go b/google/resource_certificate_manager_certificate.go new file mode 100644 index 00000000000..004394f184c --- /dev/null +++ b/google/resource_certificate_manager_certificate.go @@ -0,0 +1,595 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceCertificateManagerCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceCertificateManagerCertificateCreate, + Read: resourceCertificateManagerCertificateRead, + Update: resourceCertificateManagerCertificateUpdate, + Delete: resourceCertificateManagerCertificateDelete, + + Importer: &schema.ResourceImporter{ + State: resourceCertificateManagerCertificateImport, + }, + + 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: `A user-defined name of the certificate. Certificate names must be unique +The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "managed": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `Configuration and state of a Managed Certificate. +Certificate Manager provisions and renews Managed Certificates +automatically, for as long as it's authorized to do so.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_authorizations": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `Authorizations that will be used for performing domain authorization`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domains": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `The domains for which a managed SSL certificate will be generated. +Wildcard domains are only supported with DNS challenge resolution`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `State of the managed certificate resource.`, + }, + }, + }, + ExactlyOneOf: []string{"self_managed", "managed"}, + }, + "scope": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"DEFAULT", "EDGE_CACHE", ""}, false), + Description: `The scope of the certificate. + +Certificates with default scope are served from core Google data centers. +If unsure, choose this option. + +Certificates with scope EDGE_CACHE are special-purposed certificates, +served from non-core Google data centers. +Currently allowed only for managed certificates. Default value: "DEFAULT" Possible values: ["DEFAULT", "EDGE_CACHE"]`, + Default: "DEFAULT", + }, + "self_managed": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `Certificate data for a SelfManaged Certificate. +SelfManaged Certificates are uploaded by the user. Updating such +certificates before they expire remains the user's responsibility.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_pem": { + Type: schema.TypeString, + Required: true, + Description: `The certificate chain in PEM-encoded form. + +Leaf certificate comes first, followed by intermediate ones if any.`, + Sensitive: true, + }, + "private_key_pem": { + Type: schema.TypeString, + Required: true, + Description: `The private key of the leaf certificate in PEM-encoded form.`, + Sensitive: true, + }, + }, + }, + ExactlyOneOf: []string{"self_managed", "managed"}, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceCertificateManagerCertificateCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandCertificateManagerCertificateDescription(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 + } + labelsProp, err := expandCertificateManagerCertificateLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + scopeProp, err := expandCertificateManagerCertificateScope(d.Get("scope"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("scope"); !isEmptyValue(reflect.ValueOf(scopeProp)) && (ok || !reflect.DeepEqual(v, scopeProp)) { + obj["scope"] = scopeProp + } + selfManagedProp, err := expandCertificateManagerCertificateSelfManaged(d.Get("self_managed"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("self_managed"); !isEmptyValue(reflect.ValueOf(selfManagedProp)) && (ok || !reflect.DeepEqual(v, selfManagedProp)) { + obj["selfManaged"] = selfManagedProp + } + managedProp, err := expandCertificateManagerCertificateManaged(d.Get("managed"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("managed"); !isEmptyValue(reflect.ValueOf(managedProp)) && (ok || !reflect.DeepEqual(v, managedProp)) { + obj["managed"] = managedProp + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/certificates?certificateId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Certificate: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Certificate: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating Certificate: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/certificates/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = certificateManagerOperationWaitTime( + config, res, project, "Creating Certificate", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create Certificate: %s", err) + } + + log.Printf("[DEBUG] Finished creating Certificate %q: %#v", d.Id(), res) + + return resourceCertificateManagerCertificateRead(d, meta) +} + +func resourceCertificateManagerCertificateRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/certificates/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Certificate: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("CertificateManagerCertificate %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + + if err := d.Set("description", flattenCertificateManagerCertificateDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + if err := d.Set("labels", flattenCertificateManagerCertificateLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + if err := d.Set("scope", flattenCertificateManagerCertificateScope(res["scope"], d, config)); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + if err := d.Set("self_managed", flattenCertificateManagerCertificateSelfManaged(res["selfManaged"], d, config)); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + if err := d.Set("managed", flattenCertificateManagerCertificateManaged(res["managed"], d, config)); err != nil { + return fmt.Errorf("Error reading Certificate: %s", err) + } + + return nil +} + +func resourceCertificateManagerCertificateUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Certificate: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandCertificateManagerCertificateDescription(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 + } + labelsProp, err := expandCertificateManagerCertificateLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/certificates/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Certificate %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating Certificate %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Certificate %q: %#v", d.Id(), res) + } + + err = certificateManagerOperationWaitTime( + config, res, project, "Updating Certificate", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceCertificateManagerCertificateRead(d, meta) +} + +func resourceCertificateManagerCertificateDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Certificate: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/certificates/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Certificate %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Certificate") + } + + err = certificateManagerOperationWaitTime( + config, res, project, "Deleting Certificate", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Certificate %q: %#v", d.Id(), res) + return nil +} + +func resourceCertificateManagerCertificateImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/certificates/(?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}}/locations/global/certificates/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenCertificateManagerCertificateDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateScope(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateSelfManaged(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["certificate_pem"] = + flattenCertificateManagerCertificateSelfManagedCertificatePem(original["certificatePem"], d, config) + transformed["private_key_pem"] = + flattenCertificateManagerCertificateSelfManagedPrivateKeyPem(original["privateKeyPem"], d, config) + return []interface{}{transformed} +} +func flattenCertificateManagerCertificateSelfManagedCertificatePem(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateSelfManagedPrivateKeyPem(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateManaged(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["state"] = + flattenCertificateManagerCertificateManagedState(original["state"], d, config) + transformed["domains"] = + flattenCertificateManagerCertificateManagedDomains(original["domains"], d, config) + transformed["dns_authorizations"] = + flattenCertificateManagerCertificateManagedDnsAuthorizations(original["dnsAuthorizations"], d, config) + return []interface{}{transformed} +} +func flattenCertificateManagerCertificateManagedState(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateManagedDomains(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerCertificateManagedDnsAuthorizations(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return d.Get("managed.0.dns_authorizations") +} + +func expandCertificateManagerCertificateDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandCertificateManagerCertificateScope(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateSelfManaged(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{}) + + transformedCertificatePem, err := expandCertificateManagerCertificateSelfManagedCertificatePem(original["certificate_pem"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCertificatePem); val.IsValid() && !isEmptyValue(val) { + transformed["certificatePem"] = transformedCertificatePem + } + + transformedPrivateKeyPem, err := expandCertificateManagerCertificateSelfManagedPrivateKeyPem(original["private_key_pem"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrivateKeyPem); val.IsValid() && !isEmptyValue(val) { + transformed["privateKeyPem"] = transformedPrivateKeyPem + } + + return transformed, nil +} + +func expandCertificateManagerCertificateSelfManagedCertificatePem(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateSelfManagedPrivateKeyPem(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateManaged(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{}) + + transformedState, err := expandCertificateManagerCertificateManagedState(original["state"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedState); val.IsValid() && !isEmptyValue(val) { + transformed["state"] = transformedState + } + + transformedDomains, err := expandCertificateManagerCertificateManagedDomains(original["domains"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDomains); val.IsValid() && !isEmptyValue(val) { + transformed["domains"] = transformedDomains + } + + transformedDnsAuthorizations, err := expandCertificateManagerCertificateManagedDnsAuthorizations(original["dns_authorizations"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDnsAuthorizations); val.IsValid() && !isEmptyValue(val) { + transformed["dnsAuthorizations"] = transformedDnsAuthorizations + } + + return transformed, nil +} + +func expandCertificateManagerCertificateManagedState(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateManagedDomains(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerCertificateManagedDnsAuthorizations(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_certificate_manager_certificate_generated_test.go b/google/resource_certificate_manager_certificate_generated_test.go new file mode 100644 index 00000000000..8cb117391ae --- /dev/null +++ b/google/resource_certificate_manager_certificate_generated_test.go @@ -0,0 +1,115 @@ +// ---------------------------------------------------------------------------- +// +// *** 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/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCertificateManagerCertificate_certificateManagerCertificateBasicExample(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: testAccCheckCertificateManagerCertificateDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCertificateManagerCertificate_certificateManagerCertificateBasicExample(context), + }, + { + ResourceName: "google_certificate_manager_certificate.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "managed.0.dns_authorizations"}, + }, + }, + }) +} + +func testAccCertificateManagerCertificate_certificateManagerCertificateBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_certificate_manager_certificate" "default" { + name = "tf-test-dns-cert%{random_suffix}" + description = "The default cert" + scope = "EDGE_CACHE" + managed { + domains = [ + google_certificate_manager_dns_authorization.instance.domain, + google_certificate_manager_dns_authorization.instance2.domain, + ] + dns_authorizations = [ + google_certificate_manager_dns_authorization.instance.id, + google_certificate_manager_dns_authorization.instance2.id, + ] + } +} + + +resource "google_certificate_manager_dns_authorization" "instance" { + name = "tf-test-dns-auth%{random_suffix}" + description = "The default dnss" + domain = "subdomain%{random_suffix}.hashicorptest.com" +} + +resource "google_certificate_manager_dns_authorization" "instance2" { + name = "tf-test-dns-auth2%{random_suffix}" + description = "The default dnss" + domain = "subdomain2%{random_suffix}.hashicorptest.com" +} +`, context) +} + +func testAccCheckCertificateManagerCertificateDestroyProducer(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_certificate_manager_certificate" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/certificates/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("CertificateManagerCertificate still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_certificate_manager_certificate_sweeper_test.go b/google/resource_certificate_manager_certificate_sweeper_test.go new file mode 100644 index 00000000000..059b3f67e41 --- /dev/null +++ b/google/resource_certificate_manager_certificate_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/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("CertificateManagerCertificate", &resource.Sweeper{ + Name: "CertificateManagerCertificate", + F: testSweepCertificateManagerCertificate, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepCertificateManagerCertificate(region string) error { + resourceName := "CertificateManagerCertificate" + 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://certificatemanager.googleapis.com/v1alpha2/projects/{{project}}/locations/global/certificates", "?")[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, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["certificates"] + 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://certificatemanager.googleapis.com/v1alpha2/projects/{{project}}/locations/global/certificates/{{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, config.userAgent, 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_certificate_manager_dns_authorization.go b/google/resource_certificate_manager_dns_authorization.go new file mode 100644 index 00000000000..1f745d2e6b6 --- /dev/null +++ b/google/resource_certificate_manager_dns_authorization.go @@ -0,0 +1,430 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCertificateManagerDnsAuthorization() *schema.Resource { + return &schema.Resource{ + Create: resourceCertificateManagerDnsAuthorizationCreate, + Read: resourceCertificateManagerDnsAuthorizationRead, + Update: resourceCertificateManagerDnsAuthorizationUpdate, + Delete: resourceCertificateManagerDnsAuthorizationDelete, + + Importer: &schema.ResourceImporter{ + State: resourceCertificateManagerDnsAuthorizationImport, + }, + + 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{ + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `A domain which is being authorized. A DnsAuthorization resource covers a +single domain and its wildcard, e.g. authorization for "example.com" can +be used to issue certificates for "example.com" and "*.example.com".`, + }, + "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-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "dns_resource_record": { + Type: schema.TypeList, + Computed: true, + Description: `The structure describing the DNS Resource Record that needs to be added +to DNS configuration for the authorization to be usable by +certificate.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeString, + Computed: true, + Description: `Data of the DNS Resource Record.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `Fully qualified name of the DNS Resource Record.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `Type of the DNS Resource Record.`, + }, + }, + }, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceCertificateManagerDnsAuthorizationCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandCertificateManagerDnsAuthorizationDescription(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 + } + labelsProp, err := expandCertificateManagerDnsAuthorizationLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + domainProp, err := expandCertificateManagerDnsAuthorizationDomain(d.Get("domain"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("domain"); !isEmptyValue(reflect.ValueOf(domainProp)) && (ok || !reflect.DeepEqual(v, domainProp)) { + obj["domain"] = domainProp + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/dnsAuthorizations?dnsAuthorizationId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new DnsAuthorization: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DnsAuthorization: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating DnsAuthorization: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = certificateManagerOperationWaitTime( + config, res, project, "Creating DnsAuthorization", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create DnsAuthorization: %s", err) + } + + log.Printf("[DEBUG] Finished creating DnsAuthorization %q: %#v", d.Id(), res) + + return resourceCertificateManagerDnsAuthorizationRead(d, meta) +} + +func resourceCertificateManagerDnsAuthorizationRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DnsAuthorization: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("CertificateManagerDnsAuthorization %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading DnsAuthorization: %s", err) + } + + if err := d.Set("description", flattenCertificateManagerDnsAuthorizationDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading DnsAuthorization: %s", err) + } + if err := d.Set("labels", flattenCertificateManagerDnsAuthorizationLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading DnsAuthorization: %s", err) + } + if err := d.Set("domain", flattenCertificateManagerDnsAuthorizationDomain(res["domain"], d, config)); err != nil { + return fmt.Errorf("Error reading DnsAuthorization: %s", err) + } + if err := d.Set("dns_resource_record", flattenCertificateManagerDnsAuthorizationDnsResourceRecord(res["dnsResourceRecord"], d, config)); err != nil { + return fmt.Errorf("Error reading DnsAuthorization: %s", err) + } + + return nil +} + +func resourceCertificateManagerDnsAuthorizationUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DnsAuthorization: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandCertificateManagerDnsAuthorizationDescription(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 + } + labelsProp, err := expandCertificateManagerDnsAuthorizationLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating DnsAuthorization %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating DnsAuthorization %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating DnsAuthorization %q: %#v", d.Id(), res) + } + + err = certificateManagerOperationWaitTime( + config, res, project, "Updating DnsAuthorization", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceCertificateManagerDnsAuthorizationRead(d, meta) +} + +func resourceCertificateManagerDnsAuthorizationDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DnsAuthorization: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting DnsAuthorization %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "DnsAuthorization") + } + + err = certificateManagerOperationWaitTime( + config, res, project, "Deleting DnsAuthorization", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting DnsAuthorization %q: %#v", d.Id(), res) + return nil +} + +func resourceCertificateManagerDnsAuthorizationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/dnsAuthorizations/(?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}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenCertificateManagerDnsAuthorizationDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerDnsAuthorizationLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerDnsAuthorizationDomain(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerDnsAuthorizationDnsResourceRecord(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["name"] = + flattenCertificateManagerDnsAuthorizationDnsResourceRecordName(original["name"], d, config) + transformed["type"] = + flattenCertificateManagerDnsAuthorizationDnsResourceRecordType(original["type"], d, config) + transformed["data"] = + flattenCertificateManagerDnsAuthorizationDnsResourceRecordData(original["data"], d, config) + return []interface{}{transformed} +} +func flattenCertificateManagerDnsAuthorizationDnsResourceRecordName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerDnsAuthorizationDnsResourceRecordType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCertificateManagerDnsAuthorizationDnsResourceRecordData(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandCertificateManagerDnsAuthorizationDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCertificateManagerDnsAuthorizationLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandCertificateManagerDnsAuthorizationDomain(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_certificate_manager_dns_authorization_generated_test.go b/google/resource_certificate_manager_dns_authorization_generated_test.go new file mode 100644 index 00000000000..a632d0d09ca --- /dev/null +++ b/google/resource_certificate_manager_dns_authorization_generated_test.go @@ -0,0 +1,104 @@ +// ---------------------------------------------------------------------------- +// +// *** 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/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCertificateManagerDnsAuthorization_certificateManagerDnsAuthorizationBasicExample(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: testAccCheckCertificateManagerDnsAuthorizationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCertificateManagerDnsAuthorization_certificateManagerDnsAuthorizationBasicExample(context), + }, + { + ResourceName: "google_certificate_manager_dns_authorization.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccCertificateManagerDnsAuthorization_certificateManagerDnsAuthorizationBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_certificate_manager_dns_authorization" "default" { + name = "tf-test-dns-auth%{random_suffix}" + description = "The default dnss" + domain = "%{random_suffix}.hashicorptest.com" +} + +output "record_name_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.name +} + +output "record_type_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.type +} + +output "record_data_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.data +} +`, context) +} + +func testAccCheckCertificateManagerDnsAuthorizationDestroyProducer(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_certificate_manager_dns_authorization" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{CertificateManagerBasePath}}projects/{{project}}/locations/global/dnsAuthorizations/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("CertificateManagerDnsAuthorization still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_certificate_manager_dns_authorization_sweeper_test.go b/google/resource_certificate_manager_dns_authorization_sweeper_test.go new file mode 100644 index 00000000000..75c54245889 --- /dev/null +++ b/google/resource_certificate_manager_dns_authorization_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/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("CertificateManagerDnsAuthorization", &resource.Sweeper{ + Name: "CertificateManagerDnsAuthorization", + F: testSweepCertificateManagerDnsAuthorization, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepCertificateManagerDnsAuthorization(region string) error { + resourceName := "CertificateManagerDnsAuthorization" + 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://certificatemanager.googleapis.com/v1alpha2/projects/{{project}}/locations/global/dnsAuthorizations", "?")[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, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["dnsAuthorizations"] + 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://certificatemanager.googleapis.com/v1alpha2/projects/{{project}}/locations/global/dnsAuthorizations/{{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, config.userAgent, 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_network_services_edge_cache_keyset.go b/google/resource_network_services_edge_cache_keyset.go new file mode 100644 index 00000000000..de2f72e0f9a --- /dev/null +++ b/google/resource_network_services_edge_cache_keyset.go @@ -0,0 +1,459 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceNetworkServicesEdgeCacheKeyset() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkServicesEdgeCacheKeysetCreate, + Read: resourceNetworkServicesEdgeCacheKeysetRead, + Update: resourceNetworkServicesEdgeCacheKeysetUpdate, + Delete: resourceNetworkServicesEdgeCacheKeysetDelete, + + Importer: &schema.ResourceImporter{ + State: resourceNetworkServicesEdgeCacheKeysetImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * 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-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "public_key": { + Type: schema.TypeList, + Required: true, + Description: `An ordered list of Ed25519 public keys to use for validating signed requests. +You must specify at least one (1) key, and may have up to three (3) keys. + +Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key. +You should ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset.`, + MinItems: 1, + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + Description: `The ID of the public key. The ID must be 1-63 characters long, and comply with RFC1035. +The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* +which means the first character must be a letter, and all following characters must be a dash, underscore, letter or digit.`, + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: `The base64-encoded value of the Ed25519 public key. The base64 encoding can be padded (44 bytes) or unpadded (43 bytes). +Representations or encodings of the public key other than this will be rejected with an error.`, + Sensitive: true, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceNetworkServicesEdgeCacheKeysetCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheKeysetDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheKeysetLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + publicKeysProp, err := expandNetworkServicesEdgeCacheKeysetPublicKey(d.Get("public_key"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(publicKeysProp)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) { + obj["publicKeys"] = publicKeysProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets?edgeCacheKeysetId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new EdgeCacheKeyset: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheKeyset: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating EdgeCacheKeyset: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = networkServicesOperationWaitTime( + config, res, project, "Creating EdgeCacheKeyset", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create EdgeCacheKeyset: %s", err) + } + + log.Printf("[DEBUG] Finished creating EdgeCacheKeyset %q: %#v", d.Id(), res) + + return resourceNetworkServicesEdgeCacheKeysetRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheKeysetRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheKeyset: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("NetworkServicesEdgeCacheKeyset %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) + } + + if err := d.Set("description", flattenNetworkServicesEdgeCacheKeysetDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) + } + if err := d.Set("labels", flattenNetworkServicesEdgeCacheKeysetLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) + } + if err := d.Set("public_key", flattenNetworkServicesEdgeCacheKeysetPublicKey(res["publicKeys"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) + } + + return nil +} + +func resourceNetworkServicesEdgeCacheKeysetUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheKeyset: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheKeysetDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheKeysetLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + publicKeysProp, err := expandNetworkServicesEdgeCacheKeysetPublicKey(d.Get("public_key"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) { + obj["publicKeys"] = publicKeysProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating EdgeCacheKeyset %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + + if d.HasChange("public_key") { + updateMask = append(updateMask, "publicKeys") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating EdgeCacheKeyset %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating EdgeCacheKeyset %q: %#v", d.Id(), res) + } + + err = networkServicesOperationWaitTime( + config, res, project, "Updating EdgeCacheKeyset", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceNetworkServicesEdgeCacheKeysetRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheKeysetDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheKeyset: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting EdgeCacheKeyset %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "EdgeCacheKeyset") + } + + err = networkServicesOperationWaitTime( + config, res, project, "Deleting EdgeCacheKeyset", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting EdgeCacheKeyset %q: %#v", d.Id(), res) + return nil +} + +func resourceNetworkServicesEdgeCacheKeysetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/edgeCacheKeysets/(?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}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenNetworkServicesEdgeCacheKeysetDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheKeysetLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheKeysetPublicKey(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{}{ + "id": flattenNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config), + "value": flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(original["value"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheKeysetPublicKeyId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandNetworkServicesEdgeCacheKeysetDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheKeysetLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandNetworkServicesEdgeCacheKeysetPublicKey(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{}) + + transformedId, err := expandNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedId); val.IsValid() && !isEmptyValue(val) { + transformed["id"] = transformedId + } + + transformedValue, err := expandNetworkServicesEdgeCacheKeysetPublicKeyValue(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 expandNetworkServicesEdgeCacheKeysetPublicKeyId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_network_services_edge_cache_keyset_generated_test.go b/google/resource_network_services_edge_cache_keyset_generated_test.go new file mode 100644 index 00000000000..55bc895166e --- /dev/null +++ b/google/resource_network_services_edge_cache_keyset_generated_test.go @@ -0,0 +1,100 @@ +// ---------------------------------------------------------------------------- +// +// *** 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/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetBasicExample(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: testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetBasicExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_keyset.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetBasicExample(context map[string]interface{}) string { + return Nprintf(` + +resource "google_network_services_edge_cache_keyset" "default" { + name = "default%{random_suffix}" + description = "The default keyset" + public_key { + id = "my-public-key" + value = "FHsTyFHNmvNpw4o7-rp-M1yqMyBF8vXSBRkZtkQ0RKY" + } + public_key { + id = "my-public-key-2" + value = "hzd03llxB1u5FOLKFkZ6_wCJqC7jtN0bg7xlBqS6WVM" + } +} +`, context) +} + +func testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(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_network_services_edge_cache_keyset" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("NetworkServicesEdgeCacheKeyset still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_network_services_edge_cache_keyset_sweeper_test.go b/google/resource_network_services_edge_cache_keyset_sweeper_test.go new file mode 100644 index 00000000000..6dc26c19b67 --- /dev/null +++ b/google/resource_network_services_edge_cache_keyset_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/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("NetworkServicesEdgeCacheKeyset", &resource.Sweeper{ + Name: "NetworkServicesEdgeCacheKeyset", + F: testSweepNetworkServicesEdgeCacheKeyset, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepNetworkServicesEdgeCacheKeyset(region string) error { + resourceName := "NetworkServicesEdgeCacheKeyset" + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheKeysets", "?")[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, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["edgeCacheKeysets"] + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheKeysets/{{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, config.userAgent, 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_network_services_edge_cache_origin.go b/google/resource_network_services_edge_cache_origin.go new file mode 100644 index 00000000000..e9ccf9fe52a --- /dev/null +++ b/google/resource_network_services_edge_cache_origin.go @@ -0,0 +1,710 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceNetworkServicesEdgeCacheOrigin() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkServicesEdgeCacheOriginCreate, + Read: resourceNetworkServicesEdgeCacheOriginRead, + Update: resourceNetworkServicesEdgeCacheOriginUpdate, + Delete: resourceNetworkServicesEdgeCacheOriginDelete, + + Importer: &schema.ResourceImporter{ + State: resourceNetworkServicesEdgeCacheOriginImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * 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-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "origin_address": { + Type: schema.TypeString, + Required: true, + Description: `A fully qualified domain name (FQDN) or IP address reachable over the public Internet, or the address of a Google Cloud Storage bucket. + +This address will be used as the origin for cache requests - e.g. FQDN: media-backend.example.com IPv4:35.218.1.1 IPv6:[2607:f8b0:4012:809::200e] Cloud Storage: gs://bucketname + +When providing an FQDN (hostname), it must be publicly resolvable (e.g. via Google public DNS) and IP addresses must be publicly routable. +If a Cloud Storage bucket is provided, it must be in the canonical "gs://bucketname" format. Other forms, such as "storage.googleapis.com", will be rejected.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "failover_origin": { + Type: schema.TypeString, + Optional: true, + Description: `The Origin resource to try when the current origin cannot be reached. +After maxAttempts is reached, the configured failoverOrigin will be used to fulfil the request. + +The value of timeout.maxAttemptsTimeout dictates the timeout across all origins. +A reference to a Topic resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "max_attempts": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 4), + Description: `The maximum number of attempts to cache fill from this origin. Another attempt is made when a cache fill fails with one of the retryConditions. + +Once maxAttempts to this origin have failed the failoverOrigin will be used, if one is specified. That failoverOrigin may specify its own maxAttempts, +retryConditions and failoverOrigin to control its own cache fill failures. + +The total number of allowed attempts to cache fill across this and failover origins is limited to four. +The total time allowed for cache fill attempts across this and failover origins can be controlled with maxAttemptsTimeout. + +The last valid response from an origin will be returned to the client. +If no origin returns a valid response, an HTTP 503 will be returned to the client. + +Defaults to 1. Must be a value greater than 0 and less than 4.`, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: `The port to connect to the origin on. +Defaults to port 443 for HTTP2 and HTTPS protocols, and port 80 for HTTP.`, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"HTTP2", "HTTPS", "HTTP", ""}, false), + Description: `The protocol to use to connect to the configured origin. Defaults to HTTP2, and it is strongly recommended that users use HTTP2 for both security & performance. + +When using HTTP2 or HTTPS as the protocol, a valid, publicly-signed, unexpired TLS (SSL) certificate must be presented by the origin server. Possible values: ["HTTP2", "HTTPS", "HTTP"]`, + }, + "retry_conditions": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Specifies one or more retry conditions for the configured origin. + +If the failure mode during a connection attempt to the origin matches the configured retryCondition(s), +the origin request will be retried up to maxAttempts times. The failoverOrigin, if configured, will then be used to satisfy the request. + +The default retryCondition is "CONNECT_FAILURE". + +retryConditions apply to this origin, and not subsequent failoverOrigin(s), +which may specify their own retryConditions and maxAttempts. + +Valid values are: + +- CONNECT_FAILURE: Retry on failures connecting to origins, for example due to connection timeouts. +- HTTP_5XX: Retry if the origin responds with any 5xx response code, or if the origin 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. +- RETRIABLE_4XX: Retry for retriable 4xx response codes, which include HTTP 409 (Conflict) and HTTP 429 (Too Many Requests) +- NOT_FOUND: Retry if the origin returns a HTTP 404 (Not Found). This can be useful when generating video content, and the segment is not available yet. Possible values: ["CONNECT_FAILURE", "HTTP_5XX", "GATEWAY_ERROR", "RETRIABLE_4XX", "NOT_FOUND"]`, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"CONNECT_FAILURE", "HTTP_5XX", "GATEWAY_ERROR", "RETRIABLE_4XX", "NOT_FOUND"}, false), + }, + }, + "timeout": { + Type: schema.TypeList, + Optional: true, + Description: `The connection and HTTP timeout configuration for this origin.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connect_timeout": { + Type: schema.TypeString, + Optional: true, + Description: `The maximum duration to wait for the origin connection to be established, including DNS lookup, TLS handshake and TCP/QUIC connection establishment. + +Defaults to 5 seconds. The timeout must be a value between 1s and 15s.`, + AtLeastOneOf: []string{"timeout.0.connect_timeout", "timeout.0.max_attempts_timeout", "timeout.0.response_timeout"}, + }, + "max_attempts_timeout": { + Type: schema.TypeString, + Optional: true, + Description: `The maximum time across all connection attempts to the origin, including failover origins, before returning an error to the client. A HTTP 503 will be returned if the timeout is reached before a response is returned. + +Defaults to 5 seconds. The timeout must be a value between 1s and 15s.`, + AtLeastOneOf: []string{"timeout.0.connect_timeout", "timeout.0.max_attempts_timeout", "timeout.0.response_timeout"}, + }, + "response_timeout": { + Type: schema.TypeString, + Optional: true, + Description: `The maximum duration to wait for data to arrive when reading from the HTTP connection/stream. + +Defaults to 5 seconds. The timeout must be a value between 1s and 30s.`, + AtLeastOneOf: []string{"timeout.0.connect_timeout", "timeout.0.max_attempts_timeout", "timeout.0.response_timeout"}, + }, + }, + }, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceNetworkServicesEdgeCacheOriginCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheOriginDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheOriginLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + originAddressProp, err := expandNetworkServicesEdgeCacheOriginOriginAddress(d.Get("origin_address"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("origin_address"); !isEmptyValue(reflect.ValueOf(originAddressProp)) && (ok || !reflect.DeepEqual(v, originAddressProp)) { + obj["originAddress"] = originAddressProp + } + protocolProp, err := expandNetworkServicesEdgeCacheOriginProtocol(d.Get("protocol"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("protocol"); !isEmptyValue(reflect.ValueOf(protocolProp)) && (ok || !reflect.DeepEqual(v, protocolProp)) { + obj["protocol"] = protocolProp + } + portProp, err := expandNetworkServicesEdgeCacheOriginPort(d.Get("port"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("port"); !isEmptyValue(reflect.ValueOf(portProp)) && (ok || !reflect.DeepEqual(v, portProp)) { + obj["port"] = portProp + } + maxAttemptsProp, err := expandNetworkServicesEdgeCacheOriginMaxAttempts(d.Get("max_attempts"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("max_attempts"); !isEmptyValue(reflect.ValueOf(maxAttemptsProp)) && (ok || !reflect.DeepEqual(v, maxAttemptsProp)) { + obj["maxAttempts"] = maxAttemptsProp + } + failoverOriginProp, err := expandNetworkServicesEdgeCacheOriginFailoverOrigin(d.Get("failover_origin"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("failover_origin"); !isEmptyValue(reflect.ValueOf(failoverOriginProp)) && (ok || !reflect.DeepEqual(v, failoverOriginProp)) { + obj["failoverOrigin"] = failoverOriginProp + } + retryConditionsProp, err := expandNetworkServicesEdgeCacheOriginRetryConditions(d.Get("retry_conditions"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("retry_conditions"); !isEmptyValue(reflect.ValueOf(retryConditionsProp)) && (ok || !reflect.DeepEqual(v, retryConditionsProp)) { + obj["retryConditions"] = retryConditionsProp + } + timeoutProp, err := expandNetworkServicesEdgeCacheOriginTimeout(d.Get("timeout"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("timeout"); !isEmptyValue(reflect.ValueOf(timeoutProp)) && (ok || !reflect.DeepEqual(v, timeoutProp)) { + obj["timeout"] = timeoutProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheOrigins?edgeCacheOriginId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new EdgeCacheOrigin: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheOrigin: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating EdgeCacheOrigin: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = networkServicesOperationWaitTime( + config, res, project, "Creating EdgeCacheOrigin", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create EdgeCacheOrigin: %s", err) + } + + log.Printf("[DEBUG] Finished creating EdgeCacheOrigin %q: %#v", d.Id(), res) + + return resourceNetworkServicesEdgeCacheOriginRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheOriginRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheOrigin: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("NetworkServicesEdgeCacheOrigin %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + + if err := d.Set("description", flattenNetworkServicesEdgeCacheOriginDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("labels", flattenNetworkServicesEdgeCacheOriginLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("origin_address", flattenNetworkServicesEdgeCacheOriginOriginAddress(res["originAddress"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("protocol", flattenNetworkServicesEdgeCacheOriginProtocol(res["protocol"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("port", flattenNetworkServicesEdgeCacheOriginPort(res["port"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("max_attempts", flattenNetworkServicesEdgeCacheOriginMaxAttempts(res["maxAttempts"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("failover_origin", flattenNetworkServicesEdgeCacheOriginFailoverOrigin(res["failoverOrigin"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + if err := d.Set("retry_conditions", flattenNetworkServicesEdgeCacheOriginRetryConditions(res["retryConditions"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheOrigin: %s", err) + } + + return nil +} + +func resourceNetworkServicesEdgeCacheOriginUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheOrigin: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheOriginDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheOriginLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + originAddressProp, err := expandNetworkServicesEdgeCacheOriginOriginAddress(d.Get("origin_address"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("origin_address"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, originAddressProp)) { + obj["originAddress"] = originAddressProp + } + protocolProp, err := expandNetworkServicesEdgeCacheOriginProtocol(d.Get("protocol"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("protocol"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, protocolProp)) { + obj["protocol"] = protocolProp + } + portProp, err := expandNetworkServicesEdgeCacheOriginPort(d.Get("port"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("port"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, portProp)) { + obj["port"] = portProp + } + maxAttemptsProp, err := expandNetworkServicesEdgeCacheOriginMaxAttempts(d.Get("max_attempts"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("max_attempts"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, maxAttemptsProp)) { + obj["maxAttempts"] = maxAttemptsProp + } + failoverOriginProp, err := expandNetworkServicesEdgeCacheOriginFailoverOrigin(d.Get("failover_origin"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("failover_origin"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, failoverOriginProp)) { + obj["failoverOrigin"] = failoverOriginProp + } + retryConditionsProp, err := expandNetworkServicesEdgeCacheOriginRetryConditions(d.Get("retry_conditions"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("retry_conditions"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, retryConditionsProp)) { + obj["retryConditions"] = retryConditionsProp + } + timeoutProp, err := expandNetworkServicesEdgeCacheOriginTimeout(d.Get("timeout"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("timeout"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, timeoutProp)) { + obj["timeout"] = timeoutProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating EdgeCacheOrigin %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + + if d.HasChange("origin_address") { + updateMask = append(updateMask, "originAddress") + } + + if d.HasChange("protocol") { + updateMask = append(updateMask, "protocol") + } + + if d.HasChange("port") { + updateMask = append(updateMask, "port") + } + + if d.HasChange("max_attempts") { + updateMask = append(updateMask, "maxAttempts") + } + + if d.HasChange("failover_origin") { + updateMask = append(updateMask, "failoverOrigin") + } + + if d.HasChange("retry_conditions") { + updateMask = append(updateMask, "retryConditions") + } + + if d.HasChange("timeout") { + updateMask = append(updateMask, "timeout") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating EdgeCacheOrigin %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating EdgeCacheOrigin %q: %#v", d.Id(), res) + } + + err = networkServicesOperationWaitTime( + config, res, project, "Updating EdgeCacheOrigin", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceNetworkServicesEdgeCacheOriginRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheOriginDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheOrigin: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting EdgeCacheOrigin %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "EdgeCacheOrigin") + } + + err = networkServicesOperationWaitTime( + config, res, project, "Deleting EdgeCacheOrigin", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting EdgeCacheOrigin %q: %#v", d.Id(), res) + return nil +} + +func resourceNetworkServicesEdgeCacheOriginImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/edgeCacheOrigins/(?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}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenNetworkServicesEdgeCacheOriginDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheOriginLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheOriginOriginAddress(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheOriginProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheOriginPort(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 flattenNetworkServicesEdgeCacheOriginMaxAttempts(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 flattenNetworkServicesEdgeCacheOriginFailoverOrigin(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheOriginRetryConditions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandNetworkServicesEdgeCacheOriginDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandNetworkServicesEdgeCacheOriginOriginAddress(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginProtocol(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginMaxAttempts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginFailoverOrigin(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginRetryConditions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginTimeout(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{}) + + transformedConnectTimeout, err := expandNetworkServicesEdgeCacheOriginTimeoutConnectTimeout(original["connect_timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConnectTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["connectTimeout"] = transformedConnectTimeout + } + + transformedMaxAttemptsTimeout, err := expandNetworkServicesEdgeCacheOriginTimeoutMaxAttemptsTimeout(original["max_attempts_timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxAttemptsTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["maxAttemptsTimeout"] = transformedMaxAttemptsTimeout + } + + transformedResponseTimeout, err := expandNetworkServicesEdgeCacheOriginTimeoutResponseTimeout(original["response_timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["responseTimeout"] = transformedResponseTimeout + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheOriginTimeoutConnectTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginTimeoutMaxAttemptsTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheOriginTimeoutResponseTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_network_services_edge_cache_origin_generated_test.go b/google/resource_network_services_edge_cache_origin_generated_test.go new file mode 100644 index 00000000000..437e3377b07 --- /dev/null +++ b/google/resource_network_services_edge_cache_origin_generated_test.go @@ -0,0 +1,157 @@ +// ---------------------------------------------------------------------------- +// +// *** 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/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginBasicExample(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: testAccCheckNetworkServicesEdgeCacheOriginDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginBasicExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_origin.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"timeout", "name", "timeout"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_network_services_edge_cache_origin" "default" { + name = "default%{random_suffix}" + origin_address = "gs://media-edge-default" + description = "The default bucket for media edge test" +} +`, context) +} + +func TestAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginAdvancedExample(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: testAccCheckNetworkServicesEdgeCacheOriginDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginAdvancedExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_origin.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"timeout", "name", "timeout"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheOrigin_networkServicesEdgeCacheOriginAdvancedExample(context map[string]interface{}) string { + return Nprintf(` + +resource "google_network_services_edge_cache_origin" "fallback" { + name = "fallback%{random_suffix}" + origin_address = "gs://media-edge-fallback" + description = "The default bucket for media edge test" + max_attempts = 3 + protocol = "HTTP" + port = 80 + + retry_conditions = [ + "CONNECT_FAILURE", + "NOT_FOUND", + "HTTP_5XX" + ] + timeout { + connect_timeout = "10s" + max_attempts_timeout = "10s" + response_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_origin" "default" { + name = "default%{random_suffix}" + origin_address = "gs://media-edge-default" + failover_origin = google_network_services_edge_cache_origin.fallback.id + description = "The default bucket for media edge test" + max_attempts = 2 + labels = { + a = "b" + } + + timeout { + connect_timeout = "10s" + } +} +`, context) +} + +func testAccCheckNetworkServicesEdgeCacheOriginDestroyProducer(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_network_services_edge_cache_origin" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheOrigins/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("NetworkServicesEdgeCacheOrigin still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_network_services_edge_cache_origin_sweeper_test.go b/google/resource_network_services_edge_cache_origin_sweeper_test.go new file mode 100644 index 00000000000..6f05925fd98 --- /dev/null +++ b/google/resource_network_services_edge_cache_origin_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/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("NetworkServicesEdgeCacheOrigin", &resource.Sweeper{ + Name: "NetworkServicesEdgeCacheOrigin", + F: testSweepNetworkServicesEdgeCacheOrigin, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepNetworkServicesEdgeCacheOrigin(region string) error { + resourceName := "NetworkServicesEdgeCacheOrigin" + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheOrigins", "?")[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, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["edgeCacheOrigins"] + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheOrigins/{{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, config.userAgent, 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_network_services_edge_cache_origin_test.go b/google/resource_network_services_edge_cache_origin_test.go new file mode 100644 index 00000000000..9b9138678f4 --- /dev/null +++ b/google/resource_network_services_edge_cache_origin_test.go @@ -0,0 +1,67 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccNetworkServicesEdgeCacheOrigin_updateAndImport(t *testing.T) { + t.Parallel() + name := "tf-test-origin-" + randString(t, 10) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkServicesEdgeCacheOriginDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheOrigin_update_0(name), + }, + { + ResourceName: "google_network_services_edge_cache_origin.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "timeout"}, + }, + { + Config: testAccNetworkServicesEdgeCacheOrigin_update_1(name), + }, + { + ResourceName: "google_network_services_edge_cache_origin.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "timeout"}, + }, + }, + }) +} +func testAccNetworkServicesEdgeCacheOrigin_update_0(name string) string { + return fmt.Sprintf(` + resource "google_network_services_edge_cache_origin" "instance" { + name = "%s" + origin_address = "gs://media-edge-default" + description = "The default bucket for media edge test" + max_attempts = 2 + labels = { + a = "b" + } + timeout { + connect_timeout = "10s" + } + } +`, name) +} +func testAccNetworkServicesEdgeCacheOrigin_update_1(name string) string { + return fmt.Sprintf(` + resource "google_network_services_edge_cache_origin" "instance" { + name = "%s" + origin_address = "gs://media-edge-fallback" + description = "The default bucket for media edge test" + max_attempts = 3 + timeout { + connect_timeout = "9s" + } + } +`, name) +} diff --git a/google/resource_network_services_edge_cache_service.go b/google/resource_network_services_edge_cache_service.go new file mode 100644 index 00000000000..929c0abafa1 --- /dev/null +++ b/google/resource_network_services_edge_cache_service.go @@ -0,0 +1,2972 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceNetworkServicesEdgeCacheService() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkServicesEdgeCacheServiceCreate, + Read: resourceNetworkServicesEdgeCacheServiceRead, + Update: resourceNetworkServicesEdgeCacheServiceUpdate, + Delete: resourceNetworkServicesEdgeCacheServiceDelete, + + Importer: &schema.ResourceImporter{ + State: resourceNetworkServicesEdgeCacheServiceImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * 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-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "routing": { + Type: schema.TypeList, + Required: true, + Description: `Defines how requests are routed, modified, cached and/or which origin content is filled from.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_rule": { + Type: schema.TypeList, + Required: true, + Description: `The list of hostRules to match against. These rules define which hostnames the EdgeCacheService will match against, and which route configurations apply.`, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hosts": { + Type: schema.TypeList, + Required: true, + Description: `The list of host patterns to match. + +Host patterns must be valid hostnames with optional port numbers in the format host:port. * matches any string of ([a-z0-9-.]*). +The only accepted ports are :80 and :443. + +Hosts are matched against the HTTP Host header, or for HTTP/2 and HTTP/3, the ":authority" header, from the incoming request.`, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "path_matcher": { + Type: schema.TypeString, + Required: true, + Description: `The name of the pathMatcher associated with this hostRule.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the hostRule.`, + }, + }, + }, + }, + "path_matcher": { + Type: schema.TypeList, + Required: true, + Description: `The list of pathMatchers referenced via name by hostRules. PathMatcher is used to match the path portion of the URL when a HostRule matches the URL's host portion.`, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `The name to which this PathMatcher is referred by the HostRule.`, + }, + "route_rule": { + Type: schema.TypeList, + Required: true, + Description: `The routeRules to match against. routeRules support advanced routing behaviour, and can match on paths, headers and query parameters, as well as status codes and HTTP methods.`, + MinItems: 1, + MaxItems: 64, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_rule": { + Type: schema.TypeList, + Required: true, + Description: `The list of criteria for matching attributes of a request to this routeRule. This list has OR semantics: the request matches this routeRule when any of the matchRules are satisfied. However predicates +within a given matchRule have AND semantics. All predicates within a matchRule must match for the request to match the rule.`, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "full_path_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satisfying 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.`, + }, + "header_match": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies a list of header match criteria, all of which must match corresponding headers in the request.`, + MinItems: 1, + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The header name to match on.`, + }, + "exact_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header should exactly match contents of exactMatch.`, + }, + "invert_match": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `If set to false (default), 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.`, + }, + "prefix_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header must start with the contents of prefixMatch.`, + }, + "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.`, + }, + "suffix_match": { + Type: schema.TypeString, + Optional: true, + Description: `The value of the header must end with the contents of suffixMatch.`, + }, + }, + }, + }, + "ignore_case": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `Specifies that prefixMatch and fullPathMatch matches are case sensitive.`, + }, + "path_template_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satisfying the matchRule condition, the path of the request +must match the wildcard pattern specified in pathTemplateMatch +after removing any query parameters and anchor that may be part +of the original URL. + +pathTemplateMatch must be between 1 and 255 characters +(inclusive). The pattern specified by pathTemplateMatch may +have at most 5 wildcard operators and at most 5 variable +captures in total.`, + }, + "prefix_match": { + Type: schema.TypeString, + Optional: true, + Description: `For satisfying the matchRule condition, the request's path must begin with the specified prefixMatch. prefixMatch must begin with a /.`, + }, + "query_parameter_match": { + 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.`, + MinItems: 1, + MaxItems: 5, + 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.`, + }, + "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.`, + }, + }, + }, + }, + }, + }, + }, + "priority": { + Type: schema.TypeString, + Required: true, + Description: `The priority of this route rule, where 1 is the highest priority. + +You cannot configure two or more routeRules with the same priority. Priority for each rule must be set to a number between 1 and 999 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.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the routeRule.`, + }, + "header_action": { + Type: schema.TypeList, + Optional: true, + Description: `The header actions, including adding & removing headers, for requests that match this route.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_header_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Describes a header to add.`, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header to add.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `Whether to replace all existing headers with the same name.`, + }, + }, + }, + }, + "request_header_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 origin.`, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header to remove.`, + }, + }, + }, + }, + "response_header_to_add": { + Type: schema.TypeList, + Optional: true, + Description: `Headers to add to the response prior to sending it back to the client. + +Response headers are only sent to the client, and do not have an effect on the cache serving the response.`, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `The name of the header to add.`, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + Description: `The value of the header to add.`, + }, + "replace": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `Whether to replace all existing headers with the same name.`, + }, + }, + }, + }, + "response_header_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 origin.`, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_name": { + Type: schema.TypeString, + Required: true, + Description: `Headers to remove from the response prior to sending it back to the client. + +Response headers are only sent to the client, and do not have an effect on the cache serving the response.`, + }, + }, + }, + }, + }, + }, + }, + "origin": { + Type: schema.TypeString, + Optional: true, + Description: `The Origin resource that requests to this route should fetch from when a matching response is not in cache. Origins can be defined as short names ("my-origin") or fully-qualified resource URLs - e.g. "networkservices.googleapis.com/projects/my-project/global/edgecacheorigins/my-origin" + +Only one of origin or urlRedirect can be set.`, + }, + "route_action": { + Type: schema.TypeList, + Optional: true, + Description: `In response to a matching path, the routeAction performs advanced routing actions like URL rewrites, header transformations, etc. prior to forwarding the request to the selected origin.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cdn_policy": { + Type: schema.TypeList, + Optional: true, + Description: `The policy to use for defining caching and signed request behaviour for requests that match this route.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cache_key_policy": { + Type: schema.TypeList, + Optional: true, + Description: `Defines the request parameters that contribute to the cache key.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "exclude_host": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `If true, requests to different hosts will be cached separately. + +Note: this should only be enabled if hosts share the same origin and content Removing the host from the cache key may inadvertently result in different objects being cached than intended, depending on which route the first user matched.`, + }, + "exclude_query_string": { + Type: schema.TypeBool, + Optional: true, + Description: `If true, exclude query string parameters from the cache key + +If false (the default), include the query string parameters in +the cache key according to includeQueryParameters and +excludeQueryParameters. If neither includeQueryParameters nor +excludeQueryParameters is set, the entire query string will be +included.`, + }, + "excluded_query_parameters": { + Type: schema.TypeList, + Optional: true, + Description: `Names of query string parameters to exclude from cache keys. All other parameters will be included. + +Either specify includedQueryParameters or excludedQueryParameters, not both. '&' and '=' will be percent encoded and not treated as delimiters.`, + MaxItems: 10, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "include_protocol": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `If true, http and https requests will be cached separately.`, + }, + "included_header_names": { + Type: schema.TypeList, + Optional: true, + Description: `Names of HTTP request headers to include in cache keys. The value of the header field will be used as part of the cache key. + +- Header names must be valid HTTP RFC 7230 header field values. +- Header field names are case insensitive +- To include the HTTP method, use ":method" + +Note that specifying several headers, and/or headers that have a large range of values (e.g. per-user) will dramatically impact the cache hit rate, and may result in a higher eviction rate and reduced performance.`, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "included_query_parameters": { + Type: schema.TypeList, + Optional: true, + Description: `Names of query string parameters to include in cache keys. All other parameters will be excluded. + +Either specify includedQueryParameters or excludedQueryParameters, not both. '&' and '=' will be percent encoded and not treated as delimiters.`, + MaxItems: 10, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "cache_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"CACHE_ALL_STATIC", "USE_ORIGIN_HEADERS", "FORCE_CACHE_ALL", "BYPASS_CACHE", ""}, false), + Description: `Cache modes allow users to control the behaviour of the cache, what content it should cache automatically, whether to respect origin headers, or whether to unconditionally cache all responses. + +For all cache modes, Cache-Control headers will be passed to the client. Use clientTtl to override what is sent to the client. Possible values: ["CACHE_ALL_STATIC", "USE_ORIGIN_HEADERS", "FORCE_CACHE_ALL", "BYPASS_CACHE"]`, + }, + "client_ttl": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies a separate client (e.g. browser client) TTL, separate from the TTL used by the edge caches. Leaving this empty will use the same cache TTL for both the CDN and the client-facing response. + +- The TTL must be > 0 and <= 86400s (1 day) +- The clientTtl cannot be larger than the defaultTtl (if set) +- Fractions of a second are not allowed. +- Omit this field to use the defaultTtl, or the max-age set by the origin, as the client-facing TTL. + +When the cache mode is set to "USE_ORIGIN_HEADERS" or "BYPASS_CACHE", you must omit this field. +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + "default_ttl": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Specifies the default TTL for cached content served by this origin for responses that do not have an existing valid TTL (max-age or s-max-age). + +Defaults to 3600s (1 hour). + +- The TTL must be >= 0 and <= 2592000s (1 month) +- Setting a TTL of "0" means "always revalidate" (equivalent to must-revalidate) +- The value of defaultTTL cannot be set to a value greater than that of maxTTL. +- Fractions of a second are not allowed. +- When the cacheMode is set to FORCE_CACHE_ALL, the defaultTTL will overwrite the TTL set in all responses. + +Note that infrequently accessed objects may be evicted from the cache before the defined TTL. Objects that expire will be revalidated with the origin. + +When the cache mode is set to "USE_ORIGIN_HEADERS" or "BYPASS_CACHE", you must omit this field. + +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + "max_ttl": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Specifies the maximum allowed TTL for cached content served by this origin. + +Defaults to 86400s (1 day). + +Cache directives that attempt to set a max-age or s-maxage higher than this, or an Expires header more than maxTtl seconds in the future will be capped at the value of maxTTL, as if it were the value of an s-maxage Cache-Control directive. + +- The TTL must be >= 0 and <= 2592000s (1 month) +- Setting a TTL of "0" means "always revalidate" +- The value of maxTtl must be equal to or greater than defaultTtl. +- Fractions of a second are not allowed. +- When the cache mode is set to "USE_ORIGIN_HEADERS", "FORCE_CACHE_ALL", or "BYPASS_CACHE", you must omit this field. + +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + "negative_caching": { + Type: schema.TypeBool, + Optional: true, + Description: `Negative caching allows per-status code TTLs to be set, in order to apply fine-grained caching for common errors or redirects. This can reduce the load on your origin and improve end-user experience by reducing response latency. + +By default, the CDNPolicy will apply the following default TTLs to these status codes: + +- HTTP 300 (Multiple Choice), 301, 308 (Permanent Redirects): 10m +- HTTP 404 (Not Found), 410 (Gone), 451 (Unavailable For Legal Reasons): 120s +- HTTP 405 (Method Not Found), 414 (URI Too Long), 501 (Not Implemented): 60s + +These defaults can be overridden in negativeCachingPolicy`, + }, + "negative_caching_policy": { + Type: schema.TypeMap, + Optional: true, + Description: `Sets a cache TTL for the specified HTTP status code. negativeCaching must be enabled to configure negativeCachingPolicy. + +- Omitting the policy and leaving negativeCaching enabled will use the default TTLs for each status code, defined in negativeCaching. +- TTLs must be >= 0 (where 0 is "always revalidate") and <= 86400s (1 day) + +Note that when specifying an explicit negativeCachingPolicy, you should take care to specify a cache TTL for all response codes that you wish to cache. The CDNPolicy will not apply any default negative caching when a policy exists.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "signed_request_keyset": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `The EdgeCacheKeyset containing the set of public keys used to validate signed requests at the edge.`, + }, + "signed_request_mode": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"DISABLED", "REQUIRE_SIGNATURES", ""}, false), + Description: `Whether to enforce signed requests. The default value is DISABLED, which means all content is public, and does not authorize access. + +You must also set a signedRequestKeyset to enable signed requests. + +When set to REQUIRE_SIGNATURES, all matching requests will have their signature validated. Requests that were not signed with the corresponding private key, or that are otherwise invalid (expired, do not match the signature, IP address, or header) will be rejected with a HTTP 403 and (if enabled) logged. Possible values: ["DISABLED", "REQUIRE_SIGNATURES"]`, + }, + }, + }, + }, + "cors_policy": { + Type: schema.TypeList, + Optional: true, + Description: `CORSPolicy defines Cross-Origin-Resource-Sharing configuration, including which CORS response headers will be set.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_age": { + Type: schema.TypeString, + Required: true, + Description: `Specifies how long results of a preflight request can be cached by a client in seconds. Note that many browser clients enforce a maximum TTL of 600s (10 minutes). + +- Setting the value to -1 forces a pre-flight check for all requests (not recommended) +- A maximum TTL of 86400s can be set, but note that (as above) some clients may force pre-flight checks at a more regular interval. +- This translates to the Access-Control-Max-Age header. + +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + "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 response header.`, + }, + "allow_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Headers response header.`, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "allow_methods": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Methods response header.`, + MaxItems: 5, + 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. + +This translates to the Access-Control-Allow-Origin response header.`, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "disabled": { + Type: schema.TypeBool, + Optional: true, + Description: `If true, specifies the CORS policy is disabled. The default value is false, which indicates that the CORS policy is in effect.`, + }, + "expose_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the content for the Access-Control-Allow-Headers response header.`, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "url_rewrite": { + Type: schema.TypeList, + Optional: true, + Description: `The URL rewrite configuration for requests that match this route.`, + 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 origin, the request's host header is replaced with contents of hostRewrite.`, + }, + "path_prefix_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected origin, the matching portion of the request's path is replaced by pathPrefixRewrite.`, + }, + "path_template_rewrite": { + Type: schema.TypeString, + Optional: true, + Description: `Prior to forwarding the request to the selected origin, if the +request matched a pathTemplateMatch, the matching portion of the +request's path is replaced re-written using the pattern specified +by pathTemplateRewrite. + +pathTemplateRewrite must be between 1 and 255 characters +(inclusive), must start with a '/', and must only use variables +captured by the route's pathTemplate matchers. + +pathTemplateRewrite may only be used when all of a route's +MatchRules specify pathTemplate. + +Only one of pathPrefixRewrite and pathTemplateRewrite may be +specified.`, + }, + }, + }, + }, + }, + }, + }, + "url_redirect": { + Type: schema.TypeList, + Optional: true, + Description: `The URL redirect configuration for requests that match this route.`, + 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.`, + }, + "https_redirect": { + Type: schema.TypeBool, + Computed: true, + 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 can only be set if there is at least one (1) edgeSslCertificate set on the service.`, + }, + "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 path 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 routeRule, 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.`, + }, + "redirect_response_code": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"MOVED_PERMANENTLY_DEFAULT", "FOUND", "SEE_OTHER", "TEMPORARY_REDIRECT", "PERMANENT_REDIRECT", ""}, false), + Description: `The HTTP Status code to use for this RedirectAction. + +The 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: ["MOVED_PERMANENTLY_DEFAULT", "FOUND", "SEE_OTHER", "TEMPORARY_REDIRECT", "PERMANENT_REDIRECT"]`, + }, + "strip_query": { + Type: schema.TypeBool, + Computed: true, + 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.`, + }, + }, + }, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + }, + }, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "disable_quic": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `HTTP/3 (IETF QUIC) and Google QUIC are enabled by default.`, + }, + "edge_security_policy": { + Type: schema.TypeString, + Optional: true, + Description: `Resource URL that points at the Cloud Armor edge security policy that is applied on each request against the EdgeCacheService.`, + }, + "edge_ssl_certificates": { + Type: schema.TypeList, + Optional: true, + Description: `URLs to sslCertificate resources that are used to authenticate connections between users and the EdgeCacheService. + +Note that only "global" certificates with a "scope" of "EDGE_CACHE" can be attached to an EdgeCacheService.`, + MaxItems: 5, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "log_config": { + Type: schema.TypeList, + Optional: true, + Description: `Specifies the logging options for the traffic served by this service. If logging is enabled, logs will be exported to Cloud Logging.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `Specifies whether to enable logging for traffic served by this service.`, + }, + "sample_rate": { + Type: schema.TypeFloat, + Optional: true, + Description: `Configures the sampling rate of requests, where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. The default value is 1.0, and the value of the field must be in [0, 1]. + +This field can only be specified if logging is enabled for this service.`, + }, + }, + }, + }, + "require_tls": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `Require TLS (HTTPS) for all clients connecting to this service. + +Clients who connect over HTTP (port 80) will receive a HTTP 301 to the same URL over HTTPS (port 443). +You must have at least one (1) edgeSslCertificate specified to enable this.`, + }, + "ssl_policy": { + Type: schema.TypeString, + Optional: true, + Description: `URL of the SslPolicy resource that will be associated with the EdgeCacheService. + +If not set, the EdgeCacheService has no SSL policy configured, and will default to the "COMPATIBLE" policy.`, + }, + "ipv4_addresses": { + Type: schema.TypeList, + Computed: true, + Description: `The IPv4 addresses associated with this service. Addresses are static for the lifetime of the service.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "ipv6_addresses": { + Type: schema.TypeList, + Computed: true, + Description: `The IPv6 addresses associated with this service. Addresses are static for the lifetime of the service.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceNetworkServicesEdgeCacheServiceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheServiceDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheServiceLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + disableQuicProp, err := expandNetworkServicesEdgeCacheServiceDisableQuic(d.Get("disable_quic"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_quic"); !isEmptyValue(reflect.ValueOf(disableQuicProp)) && (ok || !reflect.DeepEqual(v, disableQuicProp)) { + obj["disableQuic"] = disableQuicProp + } + requireTlsProp, err := expandNetworkServicesEdgeCacheServiceRequireTls(d.Get("require_tls"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("require_tls"); !isEmptyValue(reflect.ValueOf(requireTlsProp)) && (ok || !reflect.DeepEqual(v, requireTlsProp)) { + obj["requireTls"] = requireTlsProp + } + edgeSslCertificatesProp, err := expandNetworkServicesEdgeCacheServiceEdgeSslCertificates(d.Get("edge_ssl_certificates"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("edge_ssl_certificates"); !isEmptyValue(reflect.ValueOf(edgeSslCertificatesProp)) && (ok || !reflect.DeepEqual(v, edgeSslCertificatesProp)) { + obj["edgeSslCertificates"] = edgeSslCertificatesProp + } + sslPolicyProp, err := expandNetworkServicesEdgeCacheServiceSslPolicy(d.Get("ssl_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ssl_policy"); !isEmptyValue(reflect.ValueOf(sslPolicyProp)) && (ok || !reflect.DeepEqual(v, sslPolicyProp)) { + obj["sslPolicy"] = sslPolicyProp + } + routingProp, err := expandNetworkServicesEdgeCacheServiceRouting(d.Get("routing"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("routing"); !isEmptyValue(reflect.ValueOf(routingProp)) && (ok || !reflect.DeepEqual(v, routingProp)) { + obj["routing"] = routingProp + } + logConfigProp, err := expandNetworkServicesEdgeCacheServiceLogConfig(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 + } + edgeSecurityPolicyProp, err := expandNetworkServicesEdgeCacheServiceEdgeSecurityPolicy(d.Get("edge_security_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("edge_security_policy"); !isEmptyValue(reflect.ValueOf(edgeSecurityPolicyProp)) && (ok || !reflect.DeepEqual(v, edgeSecurityPolicyProp)) { + obj["edgeSecurityPolicy"] = edgeSecurityPolicyProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheServices?edgeCacheServiceId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new EdgeCacheService: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheService: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating EdgeCacheService: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = networkServicesOperationWaitTime( + config, res, project, "Creating EdgeCacheService", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create EdgeCacheService: %s", err) + } + + log.Printf("[DEBUG] Finished creating EdgeCacheService %q: %#v", d.Id(), res) + + return resourceNetworkServicesEdgeCacheServiceRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheServiceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheService: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("NetworkServicesEdgeCacheService %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + + if err := d.Set("description", flattenNetworkServicesEdgeCacheServiceDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("labels", flattenNetworkServicesEdgeCacheServiceLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("disable_quic", flattenNetworkServicesEdgeCacheServiceDisableQuic(res["disableQuic"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("require_tls", flattenNetworkServicesEdgeCacheServiceRequireTls(res["requireTls"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("edge_ssl_certificates", flattenNetworkServicesEdgeCacheServiceEdgeSslCertificates(res["edgeSslCertificates"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("ssl_policy", flattenNetworkServicesEdgeCacheServiceSslPolicy(res["sslPolicy"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("ipv4_addresses", flattenNetworkServicesEdgeCacheServiceIpv4Addresses(res["ipv4Addresses"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("ipv6_addresses", flattenNetworkServicesEdgeCacheServiceIpv6Addresses(res["ipv6Addresses"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("routing", flattenNetworkServicesEdgeCacheServiceRouting(res["routing"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("log_config", flattenNetworkServicesEdgeCacheServiceLogConfig(res["logConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + if err := d.Set("edge_security_policy", flattenNetworkServicesEdgeCacheServiceEdgeSecurityPolicy(res["edgeSecurityPolicy"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheService: %s", err) + } + + return nil +} + +func resourceNetworkServicesEdgeCacheServiceUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheService: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandNetworkServicesEdgeCacheServiceDescription(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 + } + labelsProp, err := expandNetworkServicesEdgeCacheServiceLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + disableQuicProp, err := expandNetworkServicesEdgeCacheServiceDisableQuic(d.Get("disable_quic"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_quic"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, disableQuicProp)) { + obj["disableQuic"] = disableQuicProp + } + requireTlsProp, err := expandNetworkServicesEdgeCacheServiceRequireTls(d.Get("require_tls"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("require_tls"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, requireTlsProp)) { + obj["requireTls"] = requireTlsProp + } + edgeSslCertificatesProp, err := expandNetworkServicesEdgeCacheServiceEdgeSslCertificates(d.Get("edge_ssl_certificates"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("edge_ssl_certificates"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, edgeSslCertificatesProp)) { + obj["edgeSslCertificates"] = edgeSslCertificatesProp + } + sslPolicyProp, err := expandNetworkServicesEdgeCacheServiceSslPolicy(d.Get("ssl_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ssl_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, sslPolicyProp)) { + obj["sslPolicy"] = sslPolicyProp + } + routingProp, err := expandNetworkServicesEdgeCacheServiceRouting(d.Get("routing"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("routing"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, routingProp)) { + obj["routing"] = routingProp + } + logConfigProp, err := expandNetworkServicesEdgeCacheServiceLogConfig(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 + } + edgeSecurityPolicyProp, err := expandNetworkServicesEdgeCacheServiceEdgeSecurityPolicy(d.Get("edge_security_policy"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("edge_security_policy"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, edgeSecurityPolicyProp)) { + obj["edgeSecurityPolicy"] = edgeSecurityPolicyProp + } + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating EdgeCacheService %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + + if d.HasChange("disable_quic") { + updateMask = append(updateMask, "disableQuic") + } + + if d.HasChange("require_tls") { + updateMask = append(updateMask, "requireTls") + } + + if d.HasChange("edge_ssl_certificates") { + updateMask = append(updateMask, "edgeSslCertificates") + } + + if d.HasChange("ssl_policy") { + updateMask = append(updateMask, "sslPolicy") + } + + if d.HasChange("routing") { + updateMask = append(updateMask, "routing") + } + + if d.HasChange("log_config") { + updateMask = append(updateMask, "logConfig") + } + + if d.HasChange("edge_security_policy") { + updateMask = append(updateMask, "edgeSecurityPolicy") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating EdgeCacheService %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating EdgeCacheService %q: %#v", d.Id(), res) + } + + err = networkServicesOperationWaitTime( + config, res, project, "Updating EdgeCacheService", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceNetworkServicesEdgeCacheServiceRead(d, meta) +} + +func resourceNetworkServicesEdgeCacheServiceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for EdgeCacheService: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting EdgeCacheService %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "EdgeCacheService") + } + + err = networkServicesOperationWaitTime( + config, res, project, "Deleting EdgeCacheService", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting EdgeCacheService %q: %#v", d.Id(), res) + return nil +} + +func resourceNetworkServicesEdgeCacheServiceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/edgeCacheServices/(?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}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenNetworkServicesEdgeCacheServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceDisableQuic(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRequireTls(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceEdgeSslCertificates(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceSslPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceIpv4Addresses(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceIpv6Addresses(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRouting(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_rule"] = + flattenNetworkServicesEdgeCacheServiceRoutingHostRule(original["hostRules"], d, config) + transformed["path_matcher"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcher(original["pathMatchers"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingHostRule(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": flattenNetworkServicesEdgeCacheServiceRoutingHostRuleDescription(original["description"], d, config), + "hosts": flattenNetworkServicesEdgeCacheServiceRoutingHostRuleHosts(original["hosts"], d, config), + "path_matcher": flattenNetworkServicesEdgeCacheServiceRoutingHostRulePathMatcher(original["pathMatcher"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingHostRuleDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingHostRuleHosts(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingHostRulePathMatcher(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcher(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherName(original["name"], d, config), + "description": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherDescription(original["description"], d, config), + "route_rule": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRule(original["routeRules"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRule(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRulePriority(original["priority"], d, config), + "description": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleDescription(original["description"], d, config), + "match_rule": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRule(original["matchRules"], d, config), + "header_action": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderAction(original["headerAction"], d, config), + "route_action": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction(original["routeAction"], d, config), + "origin": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleOrigin(original["origin"], d, config), + "url_redirect": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirect(original["urlRedirect"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRulePriority(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRule(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{}{ + "ignore_case": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleIgnoreCase(original["ignoreCase"], d, config), + "header_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatch(original["headerMatches"], d, config), + "query_parameter_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatch(original["queryParameterMatches"], d, config), + "prefix_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePrefixMatch(original["prefixMatch"], d, config), + "path_template_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePathTemplateMatch(original["pathTemplateMatch"], d, config), + "full_path_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleFullPathMatch(original["fullPathMatch"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleIgnoreCase(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatch(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchHeaderName(original["headerName"], d, config), + "present_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPresentMatch(original["presentMatch"], d, config), + "exact_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchExactMatch(original["exactMatch"], d, config), + "prefix_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPrefixMatch(original["prefixMatch"], d, config), + "suffix_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchSuffixMatch(original["suffixMatch"], d, config), + "invert_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchInvertMatch(original["invertMatch"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPresentMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchExactMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPrefixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchSuffixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchInvertMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatch(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchName(original["name"], d, config), + "present_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchPresentMatch(original["presentMatch"], d, config), + "exact_match": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchExactMatch(original["exactMatch"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchPresentMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchExactMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePrefixMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePathTemplateMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleFullPathMatch(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderAction(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_header_to_add"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAdd(original["requestHeadersToAdd"], d, config) + transformed["response_header_to_add"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAdd(original["responseHeadersToAdd"], d, config) + transformed["request_header_to_remove"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemove(original["requestHeadersToRemove"], d, config) + transformed["response_header_to_remove"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemove(original["responseHeadersToRemove"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAdd(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderName(original["headerName"], d, config), + "header_value": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAdd(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderName(original["headerName"], d, config), + "header_value": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderValue(original["headerValue"], d, config), + "replace": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddReplace(original["replace"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddReplace(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemove(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemoveHeaderName(original["headerName"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemoveHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemove(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": flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemoveHeaderName(original["headerName"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemoveHeaderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction(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["cdn_policy"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicy(original["cdnPolicy"], d, config) + transformed["url_rewrite"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(original["urlRewrite"], d, config) + transformed["cors_policy"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicy(original["corsPolicy"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicy(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_mode"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheMode(original["cacheMode"], d, config) + transformed["client_ttl"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyClientTtl(original["clientTtl"], d, config) + transformed["default_ttl"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyDefaultTtl(original["defaultTtl"], d, config) + transformed["max_ttl"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyMaxTtl(original["maxTtl"], d, config) + transformed["cache_key_policy"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicy(original["cacheKeyPolicy"], d, config) + transformed["negative_caching"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCaching(original["negativeCaching"], d, config) + transformed["negative_caching_policy"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCachingPolicy(original["negativeCachingPolicy"], d, config) + transformed["signed_request_mode"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMode(original["signedRequestMode"], d, config) + transformed["signed_request_keyset"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestKeyset(original["signedRequestKeyset"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyClientTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyDefaultTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyMaxTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicy(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_protocol"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludeProtocol(original["includeProtocol"], d, config) + transformed["exclude_query_string"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeQueryString(original["excludeQueryString"], d, config) + transformed["exclude_host"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeHost(original["excludeHost"], d, config) + transformed["included_query_parameters"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedQueryParameters(original["includedQueryParameters"], d, config) + transformed["excluded_query_parameters"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludedQueryParameters(original["excludedQueryParameters"], d, config) + transformed["included_header_names"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedHeaderNames(original["includedHeaderNames"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludeProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeQueryString(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeHost(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedQueryParameters(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludedQueryParameters(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedHeaderNames(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCaching(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCachingPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestKeyset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(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["path_prefix_rewrite"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathPrefixRewrite(original["pathPrefixRewrite"], d, config) + transformed["host_rewrite"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewriteHostRewrite(original["hostRewrite"], d, config) + transformed["path_template_rewrite"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathTemplateRewrite(original["pathTemplateRewrite"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathPrefixRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewriteHostRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathTemplateRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicy(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["max_age"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyMaxAge(original["maxAge"], d, config) + transformed["allow_credentials"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowCredentials(original["allowCredentials"], d, config) + transformed["allow_origins"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowOrigins(original["allowOrigins"], d, config) + transformed["allow_methods"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowMethods(original["allowMethods"], d, config) + transformed["allow_headers"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowHeaders(original["allowHeaders"], d, config) + transformed["expose_headers"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyExposeHeaders(original["exposeHeaders"], d, config) + transformed["disabled"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyDisabled(original["disabled"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyMaxAge(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowCredentials(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowOrigins(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowMethods(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyExposeHeaders(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyDisabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleOrigin(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirect(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"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHostRedirect(original["hostRedirect"], d, config) + transformed["path_redirect"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPathRedirect(original["pathRedirect"], d, config) + transformed["prefix_redirect"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPrefixRedirect(original["prefixRedirect"], d, config) + transformed["redirect_response_code"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectRedirectResponseCode(original["redirectResponseCode"], d, config) + transformed["https_redirect"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHttpsRedirect(original["httpsRedirect"], d, config) + transformed["strip_query"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectStripQuery(original["stripQuery"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHostRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPathRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPrefixRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectRedirectResponseCode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHttpsRedirect(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectStripQuery(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceLogConfig(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"] = + flattenNetworkServicesEdgeCacheServiceLogConfigEnable(original["enable"], d, config) + transformed["sample_rate"] = + flattenNetworkServicesEdgeCacheServiceLogConfigSampleRate(original["sampleRate"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceLogConfigEnable(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceLogConfigSampleRate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceEdgeSecurityPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandNetworkServicesEdgeCacheServiceDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandNetworkServicesEdgeCacheServiceDisableQuic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRequireTls(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceEdgeSslCertificates(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceSslPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRouting(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{}) + + transformedHostRule, err := expandNetworkServicesEdgeCacheServiceRoutingHostRule(original["host_rule"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRule); val.IsValid() && !isEmptyValue(val) { + transformed["hostRules"] = transformedHostRule + } + + transformedPathMatcher, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcher(original["path_matcher"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathMatcher); val.IsValid() && !isEmptyValue(val) { + transformed["pathMatchers"] = transformedPathMatcher + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingHostRule(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 := expandNetworkServicesEdgeCacheServiceRoutingHostRuleDescription(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 := expandNetworkServicesEdgeCacheServiceRoutingHostRuleHosts(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 := expandNetworkServicesEdgeCacheServiceRoutingHostRulePathMatcher(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 expandNetworkServicesEdgeCacheServiceRoutingHostRuleDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingHostRuleHosts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingHostRulePathMatcher(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcher(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedDescription, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedRouteRule, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRule(original["route_rule"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRouteRule); val.IsValid() && !isEmptyValue(val) { + transformed["routeRules"] = transformedRouteRule + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRule(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRulePriority(original["priority"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPriority); val.IsValid() && !isEmptyValue(val) { + transformed["priority"] = transformedPriority + } + + transformedDescription, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedMatchRule, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRule(original["match_rule"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMatchRule); val.IsValid() && !isEmptyValue(val) { + transformed["matchRules"] = transformedMatchRule + } + + transformedHeaderAction, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderAction(original["header_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderAction); val.IsValid() && !isEmptyValue(val) { + transformed["headerAction"] = transformedHeaderAction + } + + transformedRouteAction, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction(original["route_action"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRouteAction); val.IsValid() && !isEmptyValue(val) { + transformed["routeAction"] = transformedRouteAction + } + + transformedOrigin, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleOrigin(original["origin"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedOrigin); val.IsValid() && !isEmptyValue(val) { + transformed["origin"] = transformedOrigin + } + + transformedUrlRedirect, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirect(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 expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRulePriority(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRule(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{}) + + transformedIgnoreCase, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleIgnoreCase(original["ignore_case"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIgnoreCase); val.IsValid() && !isEmptyValue(val) { + transformed["ignoreCase"] = transformedIgnoreCase + } + + transformedHeaderMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatch(original["header_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderMatch); val.IsValid() && !isEmptyValue(val) { + transformed["headerMatches"] = transformedHeaderMatch + } + + transformedQueryParameterMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatch(original["query_parameter_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedQueryParameterMatch); val.IsValid() && !isEmptyValue(val) { + transformed["queryParameterMatches"] = transformedQueryParameterMatch + } + + transformedPrefixMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePrefixMatch(original["prefix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["prefixMatch"] = transformedPrefixMatch + } + + transformedPathTemplateMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePathTemplateMatch(original["path_template_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathTemplateMatch); val.IsValid() && !isEmptyValue(val) { + transformed["pathTemplateMatch"] = transformedPathTemplateMatch + } + + transformedFullPathMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleFullPathMatch(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 + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleIgnoreCase(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatch(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + transformedPresentMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPresentMatch(original["present_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPresentMatch); val.IsValid() && !isEmptyValue(val) { + transformed["presentMatch"] = transformedPresentMatch + } + + transformedExactMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchExactMatch(original["exact_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExactMatch); val.IsValid() && !isEmptyValue(val) { + transformed["exactMatch"] = transformedExactMatch + } + + transformedPrefixMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPrefixMatch(original["prefix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrefixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["prefixMatch"] = transformedPrefixMatch + } + + transformedSuffixMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchSuffixMatch(original["suffix_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSuffixMatch); val.IsValid() && !isEmptyValue(val) { + transformed["suffixMatch"] = transformedSuffixMatch + } + + transformedInvertMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchInvertMatch(original["invert_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInvertMatch); val.IsValid() && !isEmptyValue(val) { + transformed["invertMatch"] = transformedInvertMatch + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPresentMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchExactMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchPrefixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchSuffixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleHeaderMatchInvertMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatch(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchName(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchPresentMatch(original["present_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPresentMatch); val.IsValid() && !isEmptyValue(val) { + transformed["presentMatch"] = transformedPresentMatch + } + + transformedExactMatch, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchExactMatch(original["exact_match"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExactMatch); val.IsValid() && !isEmptyValue(val) { + transformed["exactMatch"] = transformedExactMatch + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchPresentMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleQueryParameterMatchExactMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePrefixMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRulePathTemplateMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleMatchRuleFullPathMatch(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderAction(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{}) + + transformedRequestHeaderToAdd, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAdd(original["request_header_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeaderToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToAdd"] = transformedRequestHeaderToAdd + } + + transformedResponseHeaderToAdd, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAdd(original["response_header_to_add"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeaderToAdd); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToAdd"] = transformedResponseHeaderToAdd + } + + transformedRequestHeaderToRemove, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemove(original["request_header_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRequestHeaderToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["requestHeadersToRemove"] = transformedRequestHeaderToRemove + } + + transformedResponseHeaderToRemove, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemove(original["response_header_to_remove"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResponseHeaderToRemove); val.IsValid() && !isEmptyValue(val) { + transformed["responseHeadersToRemove"] = transformedResponseHeaderToRemove + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAdd(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderName(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderValue(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddReplace(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 expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAdd(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderName(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderValue(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddReplace(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 expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddHeaderValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToAddReplace(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemove(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemoveHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionRequestHeaderToRemoveHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemove(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemoveHeaderName(original["header_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderName); val.IsValid() && !isEmptyValue(val) { + transformed["headerName"] = transformedHeaderName + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleHeaderActionResponseHeaderToRemoveHeaderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction(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{}) + + transformedCdnPolicy, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicy(original["cdn_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCdnPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["cdnPolicy"] = transformedCdnPolicy + } + + transformedUrlRewrite, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(original["url_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUrlRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["urlRewrite"] = transformedUrlRewrite + } + + transformedCorsPolicy, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicy(original["cors_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCorsPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["corsPolicy"] = transformedCorsPolicy + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicy(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{}) + + transformedCacheMode, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheMode(original["cache_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCacheMode); val.IsValid() && !isEmptyValue(val) { + transformed["cacheMode"] = transformedCacheMode + } + + transformedClientTtl, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyClientTtl(original["client_ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientTtl); val.IsValid() && !isEmptyValue(val) { + transformed["clientTtl"] = transformedClientTtl + } + + transformedDefaultTtl, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyDefaultTtl(original["default_ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDefaultTtl); val.IsValid() && !isEmptyValue(val) { + transformed["defaultTtl"] = transformedDefaultTtl + } + + transformedMaxTtl, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyMaxTtl(original["max_ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxTtl); val.IsValid() && !isEmptyValue(val) { + transformed["maxTtl"] = transformedMaxTtl + } + + transformedCacheKeyPolicy, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicy(original["cache_key_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCacheKeyPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["cacheKeyPolicy"] = transformedCacheKeyPolicy + } + + transformedNegativeCaching, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCaching(original["negative_caching"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNegativeCaching); val.IsValid() && !isEmptyValue(val) { + transformed["negativeCaching"] = transformedNegativeCaching + } + + transformedNegativeCachingPolicy, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCachingPolicy(original["negative_caching_policy"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNegativeCachingPolicy); val.IsValid() && !isEmptyValue(val) { + transformed["negativeCachingPolicy"] = transformedNegativeCachingPolicy + } + + transformedSignedRequestMode, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMode(original["signed_request_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSignedRequestMode); val.IsValid() && !isEmptyValue(val) { + transformed["signedRequestMode"] = transformedSignedRequestMode + } + + transformedSignedRequestKeyset, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestKeyset(original["signed_request_keyset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSignedRequestKeyset); val.IsValid() && !isEmptyValue(val) { + transformed["signedRequestKeyset"] = transformedSignedRequestKeyset + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyClientTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyDefaultTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyMaxTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicy(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{}) + + transformedIncludeProtocol, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludeProtocol(original["include_protocol"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIncludeProtocol); val.IsValid() && !isEmptyValue(val) { + transformed["includeProtocol"] = transformedIncludeProtocol + } + + transformedExcludeQueryString, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeQueryString(original["exclude_query_string"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExcludeQueryString); val.IsValid() && !isEmptyValue(val) { + transformed["excludeQueryString"] = transformedExcludeQueryString + } + + transformedExcludeHost, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeHost(original["exclude_host"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExcludeHost); val.IsValid() && !isEmptyValue(val) { + transformed["excludeHost"] = transformedExcludeHost + } + + transformedIncludedQueryParameters, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedQueryParameters(original["included_query_parameters"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIncludedQueryParameters); val.IsValid() && !isEmptyValue(val) { + transformed["includedQueryParameters"] = transformedIncludedQueryParameters + } + + transformedExcludedQueryParameters, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludedQueryParameters(original["excluded_query_parameters"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExcludedQueryParameters); val.IsValid() && !isEmptyValue(val) { + transformed["excludedQueryParameters"] = transformedExcludedQueryParameters + } + + transformedIncludedHeaderNames, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedHeaderNames(original["included_header_names"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIncludedHeaderNames); val.IsValid() && !isEmptyValue(val) { + transformed["includedHeaderNames"] = transformedIncludedHeaderNames + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludeProtocol(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeQueryString(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludeHost(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedQueryParameters(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyExcludedQueryParameters(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheKeyPolicyIncludedHeaderNames(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCaching(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyNegativeCachingPolicy(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestKeyset(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(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{}) + + transformedPathPrefixRewrite, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathPrefixRewrite(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 + } + + transformedHostRewrite, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewriteHostRewrite(original["host_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["hostRewrite"] = transformedHostRewrite + } + + transformedPathTemplateRewrite, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathTemplateRewrite(original["path_template_rewrite"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPathTemplateRewrite); val.IsValid() && !isEmptyValue(val) { + transformed["pathTemplateRewrite"] = transformedPathTemplateRewrite + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathPrefixRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewriteHostRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewritePathTemplateRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicy(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{}) + + transformedMaxAge, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyMaxAge(original["max_age"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxAge); val.IsValid() && !isEmptyValue(val) { + transformed["maxAge"] = transformedMaxAge + } + + transformedAllowCredentials, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowCredentials(original["allow_credentials"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowCredentials); val.IsValid() && !isEmptyValue(val) { + transformed["allowCredentials"] = transformedAllowCredentials + } + + transformedAllowOrigins, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowOrigins(original["allow_origins"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowOrigins); val.IsValid() && !isEmptyValue(val) { + transformed["allowOrigins"] = transformedAllowOrigins + } + + transformedAllowMethods, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowMethods(original["allow_methods"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowMethods); val.IsValid() && !isEmptyValue(val) { + transformed["allowMethods"] = transformedAllowMethods + } + + transformedAllowHeaders, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowHeaders(original["allow_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["allowHeaders"] = transformedAllowHeaders + } + + transformedExposeHeaders, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyExposeHeaders(original["expose_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExposeHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["exposeHeaders"] = transformedExposeHeaders + } + + transformedDisabled, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyMaxAge(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowCredentials(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowOrigins(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowMethods(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyAllowHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyExposeHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCorsPolicyDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleOrigin(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirect(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHostRedirect(original["host_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHostRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["hostRedirect"] = transformedHostRedirect + } + + transformedPathRedirect, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPathRedirect(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPrefixRedirect(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 := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectRedirectResponseCode(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 + } + + transformedHttpsRedirect, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHttpsRedirect(original["https_redirect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpsRedirect); val.IsValid() && !isEmptyValue(val) { + transformed["httpsRedirect"] = transformedHttpsRedirect + } + + transformedStripQuery, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectStripQuery(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 expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHostRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPathRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectPrefixRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectRedirectResponseCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectHttpsRedirect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleUrlRedirectStripQuery(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceLogConfig(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 := expandNetworkServicesEdgeCacheServiceLogConfigEnable(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 := expandNetworkServicesEdgeCacheServiceLogConfigSampleRate(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 expandNetworkServicesEdgeCacheServiceLogConfigEnable(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceLogConfigSampleRate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceEdgeSecurityPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_network_services_edge_cache_service_generated_test.go b/google/resource_network_services_edge_cache_service_generated_test.go new file mode 100644 index 00000000000..d53cd8e4149 --- /dev/null +++ b/google/resource_network_services_edge_cache_service_generated_test.go @@ -0,0 +1,348 @@ +// ---------------------------------------------------------------------------- +// +// *** 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/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceBasicExample(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: testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceBasicExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_service.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_storage_bucket" "dest" { + name = "tf-test-my-bucket%{random_suffix}" + + force_destroy = true +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "tf-test-my-origin%{random_suffix}" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "tf-test-my-service%{random_suffix}" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + } +} +`, context) +} + +func TestAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceAdvancedExample(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: testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceAdvancedExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_service.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceAdvancedExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_storage_bucket" "dest" { + name = "tf-test-my-bucket%{random_suffix}" + force_destroy = true +} + +resource "google_network_services_edge_cache_origin" "google" { + name = "google%{random_suffix}" + origin_address = "google.com" + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "tf-test-my-origin%{random_suffix}" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "tf-test-my-service%{random_suffix}" + description = "some description" + disable_quic = true + labels = { + a = "b" + } + + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + host_rule { + description = "host rule2" + hosts = ["sslcert.tf-test2.club"] + path_matcher = "routes" + } + + host_rule { + description = "host rule3" + hosts = ["sslcert.tf-test3.club"] + path_matcher = "routesAdvanced" + } + + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + + path_matcher { + name = "routesAdvanced" + description = "an advanced ruleset" + route_rule { + description = "an advanced route rule to match against" + priority = 1 + match_rule { + prefix_match = "/potato/" + query_parameter_match { + name = "debug" + present_match = true + } + query_parameter_match { + name = "state" + exact_match = "debug" + } + } + match_rule { + full_path_match = "/apple" + } + header_action { + request_header_to_add { + header_name = "debug" + header_value = "true" + replace = true + } + request_header_to_add { + header_name = "potato" + header_value = "plant" + } + response_header_to_add { + header_name = "potato" + header_value = "plant" + replace = true + } + request_header_to_remove { + header_name = "prod" + } + response_header_to_remove { + header_name = "prod" + } + } + + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3800s" + client_ttl = "3600s" + max_ttl = "9000s" + cache_key_policy { + include_protocol = true + exclude_host = true + included_query_parameters = ["apple", "dev", "santa", "claus"] + included_header_names = ["banana"] + } + negative_caching = true + signed_request_mode = "DISABLED" + negative_caching_policy = { + "500" = "3000s" + } + } + url_rewrite { + path_prefix_rewrite = "/dev" + host_rewrite = "dev.club" + } + cors_policy { + max_age = "2500s" + allow_credentials = true + allow_origins = ["*"] + allow_methods = ["GET"] + allow_headers = ["dev"] + expose_headers = ["prod"] + } + } + } + route_rule { + description = "a second route rule to match against" + priority = 2 + match_rule { + full_path_match = "/yay" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + cache_key_policy { + excluded_query_parameters = ["dev"] + } + } + cors_policy { + max_age = "3000s" + allow_headers = ["dev"] + disabled = true + } + } + } + } + } + + log_config { + enable = true + sample_rate = 0.01 + } +} +`, context) +} + +func testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(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_network_services_edge_cache_service" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheServices/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("NetworkServicesEdgeCacheService still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_network_services_edge_cache_service_sweeper_test.go b/google/resource_network_services_edge_cache_service_sweeper_test.go new file mode 100644 index 00000000000..4112e55147f --- /dev/null +++ b/google/resource_network_services_edge_cache_service_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/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("NetworkServicesEdgeCacheService", &resource.Sweeper{ + Name: "NetworkServicesEdgeCacheService", + F: testSweepNetworkServicesEdgeCacheService, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepNetworkServicesEdgeCacheService(region string) error { + resourceName := "NetworkServicesEdgeCacheService" + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheServices", "?")[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, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["edgeCacheServices"] + 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://networkservices.googleapis.com/v1/projects/{{project}}/locations/global/edgeCacheServices/{{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, config.userAgent, 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_network_services_edge_cache_service_test.go b/google/resource_network_services_edge_cache_service_test.go new file mode 100644 index 00000000000..79d6df3bd59 --- /dev/null +++ b/google/resource_network_services_edge_cache_service_test.go @@ -0,0 +1,140 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccNetworkServicesEdgeCacheService_updateAndImport(t *testing.T) { + t.Parallel() + namebkt := "tf-test-bucket-" + randString(t, 10) + nameorigin := "tf-test-origin-" + randString(t, 10) + nameservice := "tf-test-service-" + randString(t, 10) + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheService_update_0(namebkt, nameorigin, nameservice), + }, + { + ResourceName: "google_network_services_edge_cache_service.served", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccNetworkServicesEdgeCacheService_update_1(namebkt, nameorigin, nameservice), + }, + { + ResourceName: "google_network_services_edge_cache_service.served", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func testAccNetworkServicesEdgeCacheService_update_0(bktName, originName, serviceName string) string { + return fmt.Sprintf(` + resource "google_storage_bucket" "dest" { + name = "%s" + force_destroy = true + } + resource "google_network_services_edge_cache_origin" "instance" { + name = "%s" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } + } + resource "google_network_services_edge_cache_service" "served" { + name = "%s" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + } + } +`, bktName, originName, serviceName) +} +func testAccNetworkServicesEdgeCacheService_update_1(bktName, originName, serviceName string) string { + return fmt.Sprintf(` + resource "google_storage_bucket" "dest" { + name = "%s" + force_destroy = true + } + resource "google_network_services_edge_cache_origin" "instance" { + name = "%s" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } + } + resource "google_network_services_edge_cache_service" "served" { + name = "%s" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + } + } +`, bktName, originName, serviceName) +} diff --git a/website/docs/r/access_context_manager_access_level.html.markdown b/website/docs/r/access_context_manager_access_level.html.markdown index 402c92112c1..c484a24a3ed 100644 --- a/website/docs/r/access_context_manager_access_level.html.markdown +++ b/website/docs/r/access_context_manager_access_level.html.markdown @@ -34,9 +34,9 @@ To get more information about AccessLevel, see: * [Access Policy Quickstart](https://cloud.google.com/access-context-manager/docs/quickstart) ~> **Warning:** If you are using User ADCs (Application Default Credentials) with this resource, -you must specify a `billing_project` and set `user_project_override` to true -in the provider configuration. Otherwise the ACM API will return a 403 error. -Your account must have the `serviceusage.services.use` permission on the +you must specify a `billing_project` and set `user_project_override` to true +in the provider configuration. Otherwise the ACM API will return a 403 error. +Your account must have the `serviceusage.services.use` permission on the `billing_project` you defined. ## Example Usage - Access Context Manager Access Level Basic diff --git a/website/docs/r/access_context_manager_access_level_condition.html.markdown b/website/docs/r/access_context_manager_access_level_condition.html.markdown index a1018dae5c2..2ce0ef4a123 100644 --- a/website/docs/r/access_context_manager_access_level_condition.html.markdown +++ b/website/docs/r/access_context_manager_access_level_condition.html.markdown @@ -39,9 +39,9 @@ To get more information about AccessLevelCondition, see: * [Access Policy Quickstart](https://cloud.google.com/access-context-manager/docs/quickstart) ~> **Warning:** If you are using User ADCs (Application Default Credentials) with this resource, -you must specify a `billing_project` and set `user_project_override` to true -in the provider configuration. Otherwise the ACM API will return a 403 error. -Your account must have the `serviceusage.services.use` permission on the +you must specify a `billing_project` and set `user_project_override` to true +in the provider configuration. Otherwise the ACM API will return a 403 error. +Your account must have the `serviceusage.services.use` permission on the `billing_project` you defined. ## Example Usage - Access Context Manager Access Level Condition Basic diff --git a/website/docs/r/access_context_manager_access_policy.html.markdown b/website/docs/r/access_context_manager_access_policy.html.markdown index 3f9f797af1f..4cc5d1c54c1 100644 --- a/website/docs/r/access_context_manager_access_policy.html.markdown +++ b/website/docs/r/access_context_manager_access_policy.html.markdown @@ -38,9 +38,9 @@ To get more information about AccessPolicy, see: * [Access Policy Quickstart](https://cloud.google.com/access-context-manager/docs/quickstart) ~> **Warning:** If you are using User ADCs (Application Default Credentials) with this resource, -you must specify a `billing_project` and set `user_project_override` to true -in the provider configuration. Otherwise the ACM API will return a 403 error. -Your account must have the `serviceusage.services.use` permission on the +you must specify a `billing_project` and set `user_project_override` to true +in the provider configuration. Otherwise the ACM API will return a 403 error. +Your account must have the `serviceusage.services.use` permission on the `billing_project` you defined. ## Example Usage - Access Context Manager Access Policy Basic diff --git a/website/docs/r/access_context_manager_service_perimeter_resource.html.markdown b/website/docs/r/access_context_manager_service_perimeter_resource.html.markdown index bcdd8843e31..4301dc73473 100644 --- a/website/docs/r/access_context_manager_service_perimeter_resource.html.markdown +++ b/website/docs/r/access_context_manager_service_perimeter_resource.html.markdown @@ -39,9 +39,9 @@ To get more information about ServicePerimeterResource, see: * [Service Perimeter Quickstart](https://cloud.google.com/vpc-service-controls/docs/quickstart) ~> **Warning:** If you are using User ADCs (Application Default Credentials) with this resource, -you must specify a `billing_project` and set `user_project_override` to true -in the provider configuration. Otherwise the ACM API will return a 403 error. -Your account must have the `serviceusage.services.use` permission on the +you must specify a `billing_project` and set `user_project_override` to true +in the provider configuration. Otherwise the ACM API will return a 403 error. +Your account must have the `serviceusage.services.use` permission on the `billing_project` you defined. ## Example Usage - Access Context Manager Service Perimeter Resource Basic diff --git a/website/docs/r/certificate_manager_certificate.html.markdown b/website/docs/r/certificate_manager_certificate.html.markdown new file mode 100644 index 00000000000..214a9e7a1ee --- /dev/null +++ b/website/docs/r/certificate_manager_certificate.html.markdown @@ -0,0 +1,181 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Certificate manager" +layout: "google" +page_title: "Google: google_certificate_manager_certificate" +sidebar_current: "docs-google-certificate-manager-certificate" +description: |- + Certificate represents a HTTP-reachable backend for an Certificate. +--- + +# google\_certificate\_manager\_certificate + +Certificate represents a HTTP-reachable backend for an Certificate. + + + +~> **Warning:** These resources require allow-listing to use, and are not openly available to all Cloud customers. Engage with your Cloud account team to discuss how to onboard. + +~> **Warning:** All arguments including `self_managed.certificate_pem` and `self_managed.private_key_pem` will be stored in the raw +state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + + +## Example Usage - Certificate Manager Certificate Basic + + +```hcl +resource "google_certificate_manager_certificate" "default" { + name = "dns-cert" + description = "The default cert" + scope = "EDGE_CACHE" + managed { + domains = [ + google_certificate_manager_dns_authorization.instance.domain, + google_certificate_manager_dns_authorization.instance2.domain, + ] + dns_authorizations = [ + google_certificate_manager_dns_authorization.instance.id, + google_certificate_manager_dns_authorization.instance2.id, + ] + } +} + + +resource "google_certificate_manager_dns_authorization" "instance" { + name = "dns-auth" + description = "The default dnss" + domain = "subdomain.hashicorptest.com" +} + +resource "google_certificate_manager_dns_authorization" "instance2" { + name = "dns-auth2" + description = "The default dnss" + domain = "subdomain2.hashicorptest.com" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + A user-defined name of the certificate. Certificate names must be unique + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, + and all following characters must be a dash, underscore, letter or digit. + + +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `scope` - + (Optional) + The scope of the certificate. + Certificates with default scope are served from core Google data centers. + If unsure, choose this option. + Certificates with scope EDGE_CACHE are special-purposed certificates, + served from non-core Google data centers. + Currently allowed only for managed certificates. + Default value is `DEFAULT`. + Possible values are `DEFAULT` and `EDGE_CACHE`. + +* `self_managed` - + (Optional) + Certificate data for a SelfManaged Certificate. + SelfManaged Certificates are uploaded by the user. Updating such + certificates before they expire remains the user's responsibility. + Structure is documented below. + +* `managed` - + (Optional) + Configuration and state of a Managed Certificate. + Certificate Manager provisions and renews Managed Certificates + automatically, for as long as it's authorized to do so. + 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. + + +The `self_managed` block supports: + +* `certificate_pem` - + (Required) + The certificate chain in PEM-encoded form. + Leaf certificate comes first, followed by intermediate ones if any. + **Note**: This property is sensitive and will not be displayed in the plan. + +* `private_key_pem` - + (Required) + The private key of the leaf certificate in PEM-encoded form. + **Note**: This property is sensitive and will not be displayed in the plan. + +The `managed` block supports: + +* `state` - + State of the managed certificate resource. + +* `domains` - + (Optional) + The domains for which a managed SSL certificate will be generated. + Wildcard domains are only supported with DNS challenge resolution + +* `dns_authorizations` - + (Optional) + Authorizations that will be used for performing domain authorization + +## 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}}/locations/global/certificates/{{name}}` + + +## 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 + + +Certificate can be imported using any of these accepted formats: + +``` +$ terraform import google_certificate_manager_certificate.default projects/{{project}}/locations/global/certificates/{{name}} +$ terraform import google_certificate_manager_certificate.default {{project}}/{{name}} +$ terraform import google_certificate_manager_certificate.default {{name}} +``` + +## 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/certificate_manager_dns_authorization.html.markdown b/website/docs/r/certificate_manager_dns_authorization.html.markdown new file mode 100644 index 00000000000..1fdc16be6da --- /dev/null +++ b/website/docs/r/certificate_manager_dns_authorization.html.markdown @@ -0,0 +1,138 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Certificate manager" +layout: "google" +page_title: "Google: google_certificate_manager_dns_authorization" +sidebar_current: "docs-google-certificate-manager-dns-authorization" +description: |- + DnsAuthorization represents a HTTP-reachable backend for an DnsAuthorization. +--- + +# google\_certificate\_manager\_dns\_authorization + +DnsAuthorization represents a HTTP-reachable backend for an DnsAuthorization. + + + +~> **Warning:** These resources require allow-listing to use, and are not openly available to all Cloud customers. Engage with your Cloud account team to discuss how to onboard. + + +## Example Usage - Certificate Manager Dns Authorization Basic + + +```hcl +resource "google_certificate_manager_dns_authorization" "default" { + name = "dns-auth" + description = "The default dnss" + domain = "%{random_suffix}.hashicorptest.com" +} + +output "record_name_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.name +} + +output "record_type_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.type +} + +output "record_data_to_insert" { + value = google_certificate_manager_dns_authorization.default.dns_resource_record.0.data +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `domain` - + (Required) + A domain which is being authorized. A DnsAuthorization resource covers a + single domain and its wildcard, e.g. authorization for "example.com" can + be used to issue certificates for "example.com" and "*.example.com". + +* `name` - + (Required) + Name of the resource; provided by the client when the resource is created. + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, + and all following characters must be a dash, underscore, letter or digit. + + +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `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}}/locations/global/dnsAuthorizations/{{name}}` + +* `dns_resource_record` - + The structure describing the DNS Resource Record that needs to be added + to DNS configuration for the authorization to be usable by + certificate. + Structure is documented below. + + +The `dns_resource_record` block contains: + +* `name` - + Fully qualified name of the DNS Resource Record. + +* `type` - + Type of the DNS Resource Record. + +* `data` - + Data of the DNS Resource Record. + +## 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 + + +DnsAuthorization can be imported using any of these accepted formats: + +``` +$ terraform import google_certificate_manager_dns_authorization.default projects/{{project}}/locations/global/dnsAuthorizations/{{name}} +$ terraform import google_certificate_manager_dns_authorization.default {{project}}/{{name}} +$ terraform import google_certificate_manager_dns_authorization.default {{name}} +``` + +## 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/network_services_edge_cache_keyset.html.markdown b/website/docs/r/network_services_edge_cache_keyset.html.markdown new file mode 100644 index 00000000000..5ee7979534a --- /dev/null +++ b/website/docs/r/network_services_edge_cache_keyset.html.markdown @@ -0,0 +1,134 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Network services" +layout: "google" +page_title: "Google: google_network_services_edge_cache_keyset" +sidebar_current: "docs-google-network-services-edge-cache-keyset" +description: |- + EdgeCacheKeyset represents a collection of public keys used for validating signed requests. +--- + +# google\_network\_services\_edge\_cache\_keyset + +EdgeCacheKeyset represents a collection of public keys used for validating signed requests. + + + +~> **Warning:** All arguments including `public_key.public_key.value` will be stored in the raw +state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + + +## Example Usage - Network Services Edge Cache Keyset Basic + + +```hcl + +resource "google_network_services_edge_cache_keyset" "default" { + name = "default" + description = "The default keyset" + public_key { + id = "my-public-key" + value = "FHsTyFHNmvNpw4o7-rp-M1yqMyBF8vXSBRkZtkQ0RKY" + } + public_key { + id = "my-public-key-2" + value = "hzd03llxB1u5FOLKFkZ6_wCJqC7jtN0bg7xlBqS6WVM" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `public_key` - + (Required) + An ordered list of Ed25519 public keys to use for validating signed requests. + You must specify at least one (1) key, and may have up to three (3) keys. + Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key. + You should ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset. + Structure is documented below. + +* `name` - + (Required) + Name of the resource; provided by the client when the resource is created. + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, + and all following characters must be a dash, underscore, letter or digit. + + +The `public_key` block supports: + +* `id` - + (Required) + The ID of the public key. The ID must be 1-63 characters long, and comply with RFC1035. + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* + which means the first character must be a letter, and all following characters must be a dash, underscore, letter or digit. + +* `value` - + (Required) + The base64-encoded value of the Ed25519 public key. The base64 encoding can be padded (44 bytes) or unpadded (43 bytes). + Representations or encodings of the public key other than this will be rejected with an error. + **Note**: This property is sensitive and will not be displayed in the plan. + +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `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}}/locations/global/edgeCacheKeysets/{{name}}` + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 30 minutes. +- `update` - Default is 30 minutes. +- `delete` - Default is 30 minutes. + +## Import + + +EdgeCacheKeyset can be imported using any of these accepted formats: + +``` +$ terraform import google_network_services_edge_cache_keyset.default projects/{{project}}/locations/global/edgeCacheKeysets/{{name}} +$ terraform import google_network_services_edge_cache_keyset.default {{project}}/{{name}} +$ terraform import google_network_services_edge_cache_keyset.default {{name}} +``` + +## 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/network_services_edge_cache_origin.html.markdown b/website/docs/r/network_services_edge_cache_origin.html.markdown new file mode 100644 index 00000000000..07ab144b9e2 --- /dev/null +++ b/website/docs/r/network_services_edge_cache_origin.html.markdown @@ -0,0 +1,220 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Network services" +layout: "google" +page_title: "Google: google_network_services_edge_cache_origin" +sidebar_current: "docs-google-network-services-edge-cache-origin" +description: |- + EdgeCacheOrigin represents a HTTP-reachable backend for an EdgeCacheService. +--- + +# google\_network\_services\_edge\_cache\_origin + +EdgeCacheOrigin represents a HTTP-reachable backend for an EdgeCacheService. + + + + +## Example Usage - Network Services Edge Cache Origin Basic + + +```hcl +resource "google_network_services_edge_cache_origin" "default" { + name = "default" + origin_address = "gs://media-edge-default" + description = "The default bucket for media edge test" +} +``` + +## Example Usage - Network Services Edge Cache Origin Advanced + + +```hcl + +resource "google_network_services_edge_cache_origin" "fallback" { + name = "fallback" + origin_address = "gs://media-edge-fallback" + description = "The default bucket for media edge test" + max_attempts = 3 + protocol = "HTTP" + port = 80 + + retry_conditions = [ + "CONNECT_FAILURE", + "NOT_FOUND", + "HTTP_5XX" + ] + timeout { + connect_timeout = "10s" + max_attempts_timeout = "10s" + response_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_origin" "default" { + name = "default" + origin_address = "gs://media-edge-default" + failover_origin = google_network_services_edge_cache_origin.fallback.id + description = "The default bucket for media edge test" + max_attempts = 2 + labels = { + a = "b" + } + + timeout { + connect_timeout = "10s" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `origin_address` - + (Required) + A fully qualified domain name (FQDN) or IP address reachable over the public Internet, or the address of a Google Cloud Storage bucket. + This address will be used as the origin for cache requests - e.g. FQDN: media-backend.example.com IPv4:35.218.1.1 IPv6:[2607:f8b0:4012:809::200e] Cloud Storage: gs://bucketname + When providing an FQDN (hostname), it must be publicly resolvable (e.g. via Google public DNS) and IP addresses must be publicly routable. + If a Cloud Storage bucket is provided, it must be in the canonical "gs://bucketname" format. Other forms, such as "storage.googleapis.com", will be rejected. + +* `name` - + (Required) + Name of the resource; provided by the client when the resource is created. + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, + and all following characters must be a dash, underscore, letter or digit. + + +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `protocol` - + (Optional) + The protocol to use to connect to the configured origin. Defaults to HTTP2, and it is strongly recommended that users use HTTP2 for both security & performance. + When using HTTP2 or HTTPS as the protocol, a valid, publicly-signed, unexpired TLS (SSL) certificate must be presented by the origin server. + Possible values are `HTTP2`, `HTTPS`, and `HTTP`. + +* `port` - + (Optional) + The port to connect to the origin on. + Defaults to port 443 for HTTP2 and HTTPS protocols, and port 80 for HTTP. + +* `max_attempts` - + (Optional) + The maximum number of attempts to cache fill from this origin. Another attempt is made when a cache fill fails with one of the retryConditions. + Once maxAttempts to this origin have failed the failoverOrigin will be used, if one is specified. That failoverOrigin may specify its own maxAttempts, + retryConditions and failoverOrigin to control its own cache fill failures. + The total number of allowed attempts to cache fill across this and failover origins is limited to four. + The total time allowed for cache fill attempts across this and failover origins can be controlled with maxAttemptsTimeout. + The last valid response from an origin will be returned to the client. + If no origin returns a valid response, an HTTP 503 will be returned to the client. + Defaults to 1. Must be a value greater than 0 and less than 4. + +* `failover_origin` - + (Optional) + The Origin resource to try when the current origin cannot be reached. + After maxAttempts is reached, the configured failoverOrigin will be used to fulfil the request. + The value of timeout.maxAttemptsTimeout dictates the timeout across all origins. + A reference to a Topic resource. + +* `retry_conditions` - + (Optional) + Specifies one or more retry conditions for the configured origin. + If the failure mode during a connection attempt to the origin matches the configured retryCondition(s), + the origin request will be retried up to maxAttempts times. The failoverOrigin, if configured, will then be used to satisfy the request. + The default retryCondition is "CONNECT_FAILURE". + retryConditions apply to this origin, and not subsequent failoverOrigin(s), + which may specify their own retryConditions and maxAttempts. + Valid values are: + - CONNECT_FAILURE: Retry on failures connecting to origins, for example due to connection timeouts. + - HTTP_5XX: Retry if the origin responds with any 5xx response code, or if the origin 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. + - RETRIABLE_4XX: Retry for retriable 4xx response codes, which include HTTP 409 (Conflict) and HTTP 429 (Too Many Requests) + - NOT_FOUND: Retry if the origin returns a HTTP 404 (Not Found). This can be useful when generating video content, and the segment is not available yet. + Each value may be one of `CONNECT_FAILURE`, `HTTP_5XX`, `GATEWAY_ERROR`, `RETRIABLE_4XX`, and `NOT_FOUND`. + +* `timeout` - + (Optional) + The connection and HTTP timeout configuration for this origin. + 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. + + +The `timeout` block supports: + +* `connect_timeout` - + (Optional) + The maximum duration to wait for the origin connection to be established, including DNS lookup, TLS handshake and TCP/QUIC connection establishment. + Defaults to 5 seconds. The timeout must be a value between 1s and 15s. + +* `max_attempts_timeout` - + (Optional) + The maximum time across all connection attempts to the origin, including failover origins, before returning an error to the client. A HTTP 503 will be returned if the timeout is reached before a response is returned. + Defaults to 5 seconds. The timeout must be a value between 1s and 15s. + +* `response_timeout` - + (Optional) + The maximum duration to wait for data to arrive when reading from the HTTP connection/stream. + Defaults to 5 seconds. The timeout must be a value between 1s and 30s. + +## 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}}/locations/global/edgeCacheOrigins/{{name}}` + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 30 minutes. +- `update` - Default is 30 minutes. +- `delete` - Default is 30 minutes. + +## Import + + +EdgeCacheOrigin can be imported using any of these accepted formats: + +``` +$ terraform import google_network_services_edge_cache_origin.default projects/{{project}}/locations/global/edgeCacheOrigins/{{name}} +$ terraform import google_network_services_edge_cache_origin.default {{project}}/{{name}} +$ terraform import google_network_services_edge_cache_origin.default {{name}} +``` + +## 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/network_services_edge_cache_service.html.markdown b/website/docs/r/network_services_edge_cache_service.html.markdown new file mode 100644 index 00000000000..592dbccf923 --- /dev/null +++ b/website/docs/r/network_services_edge_cache_service.html.markdown @@ -0,0 +1,847 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Network services" +layout: "google" +page_title: "Google: google_network_services_edge_cache_service" +sidebar_current: "docs-google-network-services-edge-cache-service" +description: |- + EdgeCacheService defines the IP addresses, protocols, security policies, cache policies and routing configuration. +--- + +# google\_network\_services\_edge\_cache\_service + +EdgeCacheService defines the IP addresses, protocols, security policies, cache policies and routing configuration. + + + +~> **Warning:** These resources require allow-listing to use, and are not openly available to all Cloud customers. Engage with your Cloud account team to discuss how to onboard. + + +## Example Usage - Network Services Edge Cache Service Basic + + +```hcl +resource "google_storage_bucket" "dest" { + name = "my-bucket" + + force_destroy = true +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "my-origin" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "my-service" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + } +} +``` + +## Example Usage - Network Services Edge Cache Service Advanced + + +```hcl +resource "google_storage_bucket" "dest" { + name = "my-bucket" + force_destroy = true +} + +resource "google_network_services_edge_cache_origin" "google" { + name = "google" + origin_address = "google.com" + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "my-origin" + origin_address = google_storage_bucket.dest.url + description = "The default bucket for media edge test" + max_attempts = 2 + timeout { + connect_timeout = "10s" + } +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "my-service" + description = "some description" + disable_quic = true + labels = { + a = "b" + } + + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + host_rule { + description = "host rule2" + hosts = ["sslcert.tf-test2.club"] + path_matcher = "routes" + } + + host_rule { + description = "host rule3" + hosts = ["sslcert.tf-test3.club"] + path_matcher = "routesAdvanced" + } + + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against" + priority = 1 + match_rule { + prefix_match = "/" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + } + } + header_action { + response_header_to_add { + header_name = "x-cache-status" + header_value = "{cdn_cache_status}" + } + } + } + } + + path_matcher { + name = "routesAdvanced" + description = "an advanced ruleset" + route_rule { + description = "an advanced route rule to match against" + priority = 1 + match_rule { + prefix_match = "/potato/" + query_parameter_match { + name = "debug" + present_match = true + } + query_parameter_match { + name = "state" + exact_match = "debug" + } + } + match_rule { + full_path_match = "/apple" + } + header_action { + request_header_to_add { + header_name = "debug" + header_value = "true" + replace = true + } + request_header_to_add { + header_name = "potato" + header_value = "plant" + } + response_header_to_add { + header_name = "potato" + header_value = "plant" + replace = true + } + request_header_to_remove { + header_name = "prod" + } + response_header_to_remove { + header_name = "prod" + } + } + + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3800s" + client_ttl = "3600s" + max_ttl = "9000s" + cache_key_policy { + include_protocol = true + exclude_host = true + included_query_parameters = ["apple", "dev", "santa", "claus"] + included_header_names = ["banana"] + } + negative_caching = true + signed_request_mode = "DISABLED" + negative_caching_policy = { + "500" = "3000s" + } + } + url_rewrite { + path_prefix_rewrite = "/dev" + host_rewrite = "dev.club" + } + cors_policy { + max_age = "2500s" + allow_credentials = true + allow_origins = ["*"] + allow_methods = ["GET"] + allow_headers = ["dev"] + expose_headers = ["prod"] + } + } + } + route_rule { + description = "a second route rule to match against" + priority = 2 + match_rule { + full_path_match = "/yay" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = "3600s" + cache_key_policy { + excluded_query_parameters = ["dev"] + } + } + cors_policy { + max_age = "3000s" + allow_headers = ["dev"] + disabled = true + } + } + } + } + } + + log_config { + enable = true + sample_rate = 0.01 + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `routing` - + (Required) + Defines how requests are routed, modified, cached and/or which origin content is filled from. + Structure is documented below. + +* `name` - + (Required) + Name of the resource; provided by the client when the resource is created. + The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, + and all following characters must be a dash, underscore, letter or digit. + + +The `routing` block supports: + +* `host_rule` - + (Required) + The list of hostRules to match against. These rules define which hostnames the EdgeCacheService will match against, and which route configurations apply. + Structure is documented below. + +* `path_matcher` - + (Required) + The list of pathMatchers referenced via name by hostRules. PathMatcher is used to match the path portion of the URL when a HostRule matches the URL's host portion. + Structure is documented below. + + +The `host_rule` block supports: + +* `description` - + (Optional) + A human-readable description of the hostRule. + +* `hosts` - + (Required) + The list of host patterns to match. + Host patterns must be valid hostnames with optional port numbers in the format host:port. * matches any string of ([a-z0-9-.]*). + The only accepted ports are :80 and :443. + Hosts are matched against the HTTP Host header, or for HTTP/2 and HTTP/3, the ":authority" header, from the incoming request. + +* `path_matcher` - + (Required) + The name of the pathMatcher associated with this hostRule. + +The `path_matcher` block supports: + +* `name` - + (Required) + The name to which this PathMatcher is referred by the HostRule. + +* `description` - + (Optional) + A human-readable description of the resource. + +* `route_rule` - + (Required) + The routeRules to match against. routeRules support advanced routing behaviour, and can match on paths, headers and query parameters, as well as status codes and HTTP methods. + Structure is documented below. + + +The `route_rule` block supports: + +* `priority` - + (Required) + The priority of this route rule, where 1 is the highest priority. + You cannot configure two or more routeRules with the same priority. Priority for each rule must be set to a number between 1 and 999 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. + +* `description` - + (Optional) + A human-readable description of the routeRule. + +* `match_rule` - + (Required) + The list of criteria for matching attributes of a request to this routeRule. This list has OR semantics: the request matches this routeRule when any of the matchRules are satisfied. However predicates + within a given matchRule have AND semantics. All predicates within a matchRule must match for the request to match the rule. + Structure is documented below. + +* `header_action` - + (Optional) + The header actions, including adding & removing headers, for requests that match this route. + Structure is documented below. + +* `route_action` - + (Optional) + In response to a matching path, the routeAction performs advanced routing actions like URL rewrites, header transformations, etc. prior to forwarding the request to the selected origin. + Structure is documented below. + +* `origin` - + (Optional) + The Origin resource that requests to this route should fetch from when a matching response is not in cache. Origins can be defined as short names ("my-origin") or fully-qualified resource URLs - e.g. "networkservices.googleapis.com/projects/my-project/global/edgecacheorigins/my-origin" + Only one of origin or urlRedirect can be set. + +* `url_redirect` - + (Optional) + The URL redirect configuration for requests that match this route. + Structure is documented below. + + +The `match_rule` block supports: + +* `ignore_case` - + (Optional) + Specifies that prefixMatch and fullPathMatch matches are case sensitive. + +* `header_match` - + (Optional) + Specifies a list of header match criteria, all of which must match corresponding headers in the request. + Structure is documented below. + +* `query_parameter_match` - + (Optional) + Specifies a list of query parameter match criteria, all of which must match corresponding query parameters in the request. + Structure is documented below. + +* `prefix_match` - + (Optional) + For satisfying the matchRule condition, the request's path must begin with the specified prefixMatch. prefixMatch must begin with a /. + +* `path_template_match` - + (Optional) + For satisfying the matchRule condition, the path of the request + must match the wildcard pattern specified in pathTemplateMatch + after removing any query parameters and anchor that may be part + of the original URL. + pathTemplateMatch must be between 1 and 255 characters + (inclusive). The pattern specified by pathTemplateMatch may + have at most 5 wildcard operators and at most 5 variable + captures in total. + +* `full_path_match` - + (Optional) + For satisfying 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. + + +The `header_match` block supports: + +* `header_name` - + (Required) + The header name to match on. + +* `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. + +* `exact_match` - + (Optional) + The value of the header should exactly match contents of exactMatch. + +* `prefix_match` - + (Optional) + The value of the header must start with the contents of prefixMatch. + +* `suffix_match` - + (Optional) + The value of the header must end with the contents of suffixMatch. + +* `invert_match` - + (Optional) + If set to false (default), 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. + +The `query_parameter_match` block supports: + +* `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. + +* `exact_match` - + (Optional) + The queryParameterMatch matches if the value of the parameter exactly matches the contents of exactMatch. + +The `header_action` block supports: + +* `request_header_to_add` - + (Optional) + Describes a header to add. + Structure is documented below. + +* `response_header_to_add` - + (Optional) + Headers to add to the response prior to sending it back to the client. + Response headers are only sent to the client, and do not have an effect on the cache serving the response. + Structure is documented below. + +* `request_header_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 origin. + Structure is documented below. + +* `response_header_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 origin. + Structure is documented below. + + +The `request_header_to_add` block supports: + +* `header_name` - + (Required) + The name of the header to add. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Optional) + Whether to replace all existing headers with the same name. + +The `response_header_to_add` block supports: + +* `header_name` - + (Required) + The name of the header to add. + +* `header_value` - + (Required) + The value of the header to add. + +* `replace` - + (Optional) + Whether to replace all existing headers with the same name. + +The `request_header_to_remove` block supports: + +* `header_name` - + (Required) + The name of the header to remove. + +The `response_header_to_remove` block supports: + +* `header_name` - + (Required) + Headers to remove from the response prior to sending it back to the client. + Response headers are only sent to the client, and do not have an effect on the cache serving the response. + +The `route_action` block supports: + +* `cdn_policy` - + (Optional) + The policy to use for defining caching and signed request behaviour for requests that match this route. + Structure is documented below. + +* `url_rewrite` - + (Optional) + The URL rewrite configuration for requests that match this route. + Structure is documented below. + +* `cors_policy` - + (Optional) + CORSPolicy defines Cross-Origin-Resource-Sharing configuration, including which CORS response headers will be set. + Structure is documented below. + + +The `cdn_policy` block supports: + +* `cache_mode` - + (Optional) + Cache modes allow users to control the behaviour of the cache, what content it should cache automatically, whether to respect origin headers, or whether to unconditionally cache all responses. + For all cache modes, Cache-Control headers will be passed to the client. Use clientTtl to override what is sent to the client. + Possible values are `CACHE_ALL_STATIC`, `USE_ORIGIN_HEADERS`, `FORCE_CACHE_ALL`, and `BYPASS_CACHE`. + +* `client_ttl` - + (Optional) + Specifies a separate client (e.g. browser client) TTL, separate from the TTL used by the edge caches. Leaving this empty will use the same cache TTL for both the CDN and the client-facing response. + - The TTL must be > 0 and <= 86400s (1 day) + - The clientTtl cannot be larger than the defaultTtl (if set) + - Fractions of a second are not allowed. + - Omit this field to use the defaultTtl, or the max-age set by the origin, as the client-facing TTL. + When the cache mode is set to "USE_ORIGIN_HEADERS" or "BYPASS_CACHE", you must omit this field. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `default_ttl` - + (Optional) + Specifies the default TTL for cached content served by this origin for responses that do not have an existing valid TTL (max-age or s-max-age). + Defaults to 3600s (1 hour). + - The TTL must be >= 0 and <= 2592000s (1 month) + - Setting a TTL of "0" means "always revalidate" (equivalent to must-revalidate) + - The value of defaultTTL cannot be set to a value greater than that of maxTTL. + - Fractions of a second are not allowed. + - When the cacheMode is set to FORCE_CACHE_ALL, the defaultTTL will overwrite the TTL set in all responses. + Note that infrequently accessed objects may be evicted from the cache before the defined TTL. Objects that expire will be revalidated with the origin. + When the cache mode is set to "USE_ORIGIN_HEADERS" or "BYPASS_CACHE", you must omit this field. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `max_ttl` - + (Optional) + Specifies the maximum allowed TTL for cached content served by this origin. + Defaults to 86400s (1 day). + Cache directives that attempt to set a max-age or s-maxage higher than this, or an Expires header more than maxTtl seconds in the future will be capped at the value of maxTTL, as if it were the value of an s-maxage Cache-Control directive. + - The TTL must be >= 0 and <= 2592000s (1 month) + - Setting a TTL of "0" means "always revalidate" + - The value of maxTtl must be equal to or greater than defaultTtl. + - Fractions of a second are not allowed. + - When the cache mode is set to "USE_ORIGIN_HEADERS", "FORCE_CACHE_ALL", or "BYPASS_CACHE", you must omit this field. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `cache_key_policy` - + (Optional) + Defines the request parameters that contribute to the cache key. + Structure is documented below. + +* `negative_caching` - + (Optional) + Negative caching allows per-status code TTLs to be set, in order to apply fine-grained caching for common errors or redirects. This can reduce the load on your origin and improve end-user experience by reducing response latency. + By default, the CDNPolicy will apply the following default TTLs to these status codes: + - HTTP 300 (Multiple Choice), 301, 308 (Permanent Redirects): 10m + - HTTP 404 (Not Found), 410 (Gone), 451 (Unavailable For Legal Reasons): 120s + - HTTP 405 (Method Not Found), 414 (URI Too Long), 501 (Not Implemented): 60s + These defaults can be overridden in negativeCachingPolicy + +* `negative_caching_policy` - + (Optional) + Sets a cache TTL for the specified HTTP status code. negativeCaching must be enabled to configure negativeCachingPolicy. + - Omitting the policy and leaving negativeCaching enabled will use the default TTLs for each status code, defined in negativeCaching. + - TTLs must be >= 0 (where 0 is "always revalidate") and <= 86400s (1 day) + Note that when specifying an explicit negativeCachingPolicy, you should take care to specify a cache TTL for all response codes that you wish to cache. The CDNPolicy will not apply any default negative caching when a policy exists. + +* `signed_request_mode` - + (Optional) + Whether to enforce signed requests. The default value is DISABLED, which means all content is public, and does not authorize access. + You must also set a signedRequestKeyset to enable signed requests. + When set to REQUIRE_SIGNATURES, all matching requests will have their signature validated. Requests that were not signed with the corresponding private key, or that are otherwise invalid (expired, do not match the signature, IP address, or header) will be rejected with a HTTP 403 and (if enabled) logged. + Possible values are `DISABLED` and `REQUIRE_SIGNATURES`. + +* `signed_request_keyset` - + (Optional) + The EdgeCacheKeyset containing the set of public keys used to validate signed requests at the edge. + + +The `cache_key_policy` block supports: + +* `include_protocol` - + (Optional) + If true, http and https requests will be cached separately. + +* `exclude_query_string` - + (Optional) + If true, exclude query string parameters from the cache key + If false (the default), include the query string parameters in + the cache key according to includeQueryParameters and + excludeQueryParameters. If neither includeQueryParameters nor + excludeQueryParameters is set, the entire query string will be + included. + +* `exclude_host` - + (Optional) + If true, requests to different hosts will be cached separately. + Note: this should only be enabled if hosts share the same origin and content Removing the host from the cache key may inadvertently result in different objects being cached than intended, depending on which route the first user matched. + +* `included_query_parameters` - + (Optional) + Names of query string parameters to include in cache keys. All other parameters will be excluded. + Either specify includedQueryParameters or excludedQueryParameters, not both. '&' and '=' will be percent encoded and not treated as delimiters. + +* `excluded_query_parameters` - + (Optional) + Names of query string parameters to exclude from cache keys. All other parameters will be included. + Either specify includedQueryParameters or excludedQueryParameters, not both. '&' and '=' will be percent encoded and not treated as delimiters. + +* `included_header_names` - + (Optional) + Names of HTTP request headers to include in cache keys. The value of the header field will be used as part of the cache key. + - Header names must be valid HTTP RFC 7230 header field values. + - Header field names are case insensitive + - To include the HTTP method, use ":method" + Note that specifying several headers, and/or headers that have a large range of values (e.g. per-user) will dramatically impact the cache hit rate, and may result in a higher eviction rate and reduced performance. + +The `url_rewrite` block supports: + +* `path_prefix_rewrite` - + (Optional) + Prior to forwarding the request to the selected origin, the matching portion of the request's path is replaced by pathPrefixRewrite. + +* `host_rewrite` - + (Optional) + Prior to forwarding the request to the selected origin, the request's host header is replaced with contents of hostRewrite. + +* `path_template_rewrite` - + (Optional) + Prior to forwarding the request to the selected origin, if the + request matched a pathTemplateMatch, the matching portion of the + request's path is replaced re-written using the pattern specified + by pathTemplateRewrite. + pathTemplateRewrite must be between 1 and 255 characters + (inclusive), must start with a '/', and must only use variables + captured by the route's pathTemplate matchers. + pathTemplateRewrite may only be used when all of a route's + MatchRules specify pathTemplate. + Only one of pathPrefixRewrite and pathTemplateRewrite may be + specified. + +The `cors_policy` block supports: + +* `max_age` - + (Required) + Specifies how long results of a preflight request can be cached by a client in seconds. Note that many browser clients enforce a maximum TTL of 600s (10 minutes). + - Setting the value to -1 forces a pre-flight check for all requests (not recommended) + - A maximum TTL of 86400s can be set, but note that (as above) some clients may force pre-flight checks at a more regular interval. + - This translates to the Access-Control-Max-Age header. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `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 response header. + +* `allow_origins` - + (Optional) + Specifies the list of origins that will be allowed to do CORS requests. + This translates to the Access-Control-Allow-Origin response header. + +* `allow_methods` - + (Optional) + Specifies the content for the Access-Control-Allow-Methods response header. + +* `allow_headers` - + (Optional) + Specifies the content for the Access-Control-Allow-Headers response header. + +* `expose_headers` - + (Optional) + Specifies the content for the Access-Control-Allow-Headers response header. + +* `disabled` - + (Optional) + If true, specifies the CORS policy is disabled. The default value is false, which indicates that the CORS policy is in effect. + +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. + +* `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 path value must be between 1 and 1024 characters. + +* `prefix_redirect` - + (Optional) + The prefix that replaces the prefixMatch specified in the routeRule, 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. + +* `redirect_response_code` - + (Optional) + The HTTP Status code to use for this RedirectAction. + The 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 `MOVED_PERMANENTLY_DEFAULT`, `FOUND`, `SEE_OTHER`, `TEMPORARY_REDIRECT`, and `PERMANENT_REDIRECT`. + +* `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 can only be set if there is at least one (1) edgeSslCertificate set on the service. + +* `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. + +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `disable_quic` - + (Optional) + HTTP/3 (IETF QUIC) and Google QUIC are enabled by default. + +* `require_tls` - + (Optional) + Require TLS (HTTPS) for all clients connecting to this service. + Clients who connect over HTTP (port 80) will receive a HTTP 301 to the same URL over HTTPS (port 443). + You must have at least one (1) edgeSslCertificate specified to enable this. + +* `edge_ssl_certificates` - + (Optional) + URLs to sslCertificate resources that are used to authenticate connections between users and the EdgeCacheService. + Note that only "global" certificates with a "scope" of "EDGE_CACHE" can be attached to an EdgeCacheService. + +* `ssl_policy` - + (Optional) + URL of the SslPolicy resource that will be associated with the EdgeCacheService. + If not set, the EdgeCacheService has no SSL policy configured, and will default to the "COMPATIBLE" policy. + +* `log_config` - + (Optional) + Specifies the logging options for the traffic served by this service. If logging is enabled, logs will be exported to Cloud Logging. + Structure is documented below. + +* `edge_security_policy` - + (Optional) + Resource URL that points at the Cloud Armor edge security policy that is applied on each request against the EdgeCacheService. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `log_config` block supports: + +* `enable` - + (Required) + Specifies whether to enable logging for traffic served by this service. + +* `sample_rate` - + (Optional) + Configures the sampling rate of requests, where 1.0 means all logged requests are reported and 0.0 means no logged requests are reported. The default value is 1.0, and the value of the field must be in [0, 1]. + This field can only be specified if logging is enabled for this service. + +## 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}}/locations/global/edgeCacheServices/{{name}}` + +* `ipv4_addresses` - + The IPv4 addresses associated with this service. Addresses are static for the lifetime of the service. + +* `ipv6_addresses` - + The IPv6 addresses associated with this service. Addresses are static for the lifetime of the service. + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 30 minutes. +- `update` - Default is 30 minutes. +- `delete` - Default is 30 minutes. + +## Import + + +EdgeCacheService can be imported using any of these accepted formats: + +``` +$ terraform import google_network_services_edge_cache_service.default projects/{{project}}/locations/global/edgeCacheServices/{{name}} +$ terraform import google_network_services_edge_cache_service.default {{project}}/{{name}} +$ terraform import google_network_services_edge_cache_service.default {{name}} +``` + +## 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 a58e66578de..5a047b5e743 100644 --- a/website/google.erb +++ b/website/google.erb @@ -423,6 +423,26 @@ +
  • + Certificate manager + +
  • +
  • Cloud (Stackdriver) Logging
  • +
  • + Network services + +
  • +
  • NetworkManagement