diff --git a/.changelog/4600.txt b/.changelog/4600.txt new file mode 100644 index 0000000000..b13dfc4d16 --- /dev/null +++ b/.changelog/4600.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_gke_hub_membership` +``` diff --git a/google-beta/config.go b/google-beta/config.go index 1ba0763cd8..73751cd59e 100644 --- a/google-beta/config.go +++ b/google-beta/config.go @@ -122,6 +122,7 @@ type Config struct { FirebaseBasePath string FirestoreBasePath string GameServicesBasePath string + GKEHubBasePath string HealthcareBasePath string IAMBetaBasePath string IapBasePath string @@ -219,6 +220,7 @@ var FilestoreDefaultBasePath = "https://file.googleapis.com/v1beta1/" var FirebaseDefaultBasePath = "https://firebase.googleapis.com/v1beta1/" var FirestoreDefaultBasePath = "https://firestore.googleapis.com/v1/" var GameServicesDefaultBasePath = "https://gameservices.googleapis.com/v1beta/" +var GKEHubDefaultBasePath = "https://gkehub.googleapis.com/v1beta1/" var HealthcareDefaultBasePath = "https://healthcare.googleapis.com/v1beta1/" var IAMBetaDefaultBasePath = "https://iam.googleapis.com/v1beta/" var IapDefaultBasePath = "https://iap.googleapis.com/v1/" @@ -1006,6 +1008,7 @@ func ConfigureBasePaths(c *Config) { c.FirebaseBasePath = FirebaseDefaultBasePath c.FirestoreBasePath = FirestoreDefaultBasePath c.GameServicesBasePath = GameServicesDefaultBasePath + c.GKEHubBasePath = GKEHubDefaultBasePath c.HealthcareBasePath = HealthcareDefaultBasePath c.IAMBetaBasePath = IAMBetaDefaultBasePath c.IapBasePath = IapDefaultBasePath diff --git a/google-beta/gke_hub_operation.go b/google-beta/gke_hub_operation.go new file mode 100644 index 0000000000..5df448e601 --- /dev/null +++ b/google-beta/gke_hub_operation.go @@ -0,0 +1,78 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "encoding/json" + "fmt" + "time" +) + +type GKEHubOperationWaiter struct { + Config *Config + UserAgent string + Project string + CommonOperationWaiter +} + +func (w *GKEHubOperationWaiter) 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://gkehub.googleapis.com/v1beta1/%s", w.CommonOperationWaiter.Op.Name) + + return sendRequest(w.Config, "GET", w.Project, url, w.UserAgent, nil) +} + +func createGKEHubWaiter(config *Config, op map[string]interface{}, project, activity, userAgent string) (*GKEHubOperationWaiter, error) { + if val, ok := op["name"]; !ok || val == "" { + // An operation could also be indicated with a "metadata" field. + if _, ok := op["metadata"]; !ok { + // This was a synchronous call - there is no operation to wait for. + return nil, nil + } + } + w := &GKEHubOperationWaiter{ + Config: config, + UserAgent: userAgent, + Project: project, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +// nolint: deadcode,unused +func gKEHubOperationWaitTimeWithResponse(config *Config, op map[string]interface{}, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + w, err := createGKEHubWaiter(config, op, project, activity, userAgent) + if err != nil || w == nil { + // If w is nil, the op was synchronous. + return err + } + if err := OperationWait(w, activity, timeout, config.PollInterval); err != nil { + return err + } + return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response) +} + +func gKEHubOperationWaitTime(config *Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + w, err := createGKEHubWaiter(config, op, project, activity, userAgent) + if err != nil || w == nil { + // If w is nil, the op was synchronous. + return err + } + return OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google-beta/provider.go b/google-beta/provider.go index f390bf0c8a..7205e3e064 100644 --- a/google-beta/provider.go +++ b/google-beta/provider.go @@ -439,6 +439,14 @@ func Provider() *schema.Provider { "GOOGLE_GAME_SERVICES_CUSTOM_ENDPOINT", }, GameServicesDefaultBasePath), }, + "gke_hub_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_GKE_HUB_CUSTOM_ENDPOINT", + }, GKEHubDefaultBasePath), + }, "healthcare_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -835,9 +843,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 215 +// Generated resources: 216 // Generated IAM resources: 111 -// Total generated resources: 326 +// Total generated resources: 327 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1049,6 +1057,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_game_services_game_server_deployment": resourceGameServicesGameServerDeployment(), "google_game_services_game_server_config": resourceGameServicesGameServerConfig(), "google_game_services_game_server_deployment_rollout": resourceGameServicesGameServerDeploymentRollout(), + "google_gke_hub_membership": resourceGKEHubMembership(), "google_healthcare_dataset": resourceHealthcareDataset(), "google_healthcare_dicom_store": resourceHealthcareDicomStore(), "google_healthcare_fhir_store": resourceHealthcareFhirStore(), @@ -1408,6 +1417,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.FirebaseBasePath = d.Get("firebase_custom_endpoint").(string) config.FirestoreBasePath = d.Get("firestore_custom_endpoint").(string) config.GameServicesBasePath = d.Get("game_services_custom_endpoint").(string) + config.GKEHubBasePath = d.Get("gke_hub_custom_endpoint").(string) config.HealthcareBasePath = d.Get("healthcare_custom_endpoint").(string) config.IAMBetaBasePath = d.Get("iam_beta_custom_endpoint").(string) config.IapBasePath = d.Get("iap_custom_endpoint").(string) diff --git a/google-beta/resource_gke_hub_membership.go b/google-beta/resource_gke_hub_membership.go new file mode 100644 index 0000000000..2d79affa5f --- /dev/null +++ b/google-beta/resource_gke_hub_membership.go @@ -0,0 +1,557 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 resourceGKEHubMembership() *schema.Resource { + return &schema.Resource{ + Create: resourceGKEHubMembershipCreate, + Read: resourceGKEHubMembershipRead, + Update: resourceGKEHubMembershipUpdate, + Delete: resourceGKEHubMembershipDelete, + + Importer: &schema.ResourceImporter{ + State: resourceGKEHubMembershipImport, + }, + + 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{ + "membership_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The client-provided identifier of the membership.`, + }, + "authority": { + Type: schema.TypeList, + Optional: true, + Description: `Authority encodes how Google will recognize identities from this Membership. +See the workload identity documentation for more details: +https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "issuer": { + Type: schema.TypeString, + Required: true, + Description: `A JSON Web Token (JWT) issuer URI. 'issuer' must start with 'https://' and // be a valid +with length <2000 characters.`, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `The name of this entity type to be displayed on the console.`, + }, + "endpoint": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `If this Membership is a Kubernetes API server hosted on GKE, this is a self link to its GCP resource.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "gke_cluster": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `If this Membership is a Kubernetes API server hosted on GKE, this is a self link to its GCP resource.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource_link": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Self-link of the GCP resource for the GKE cluster. +For example: '//container.googleapis.com/projects/my-project/zones/us-west1-a/clusters/my-cluster'. +It can be at the most 1000 characters in length. If the cluster is provisioned with Terraform, +this is '"//container.googleapis.com/${google_container_cluster.my-cluster.id}"'.`, + }, + }, + }, + }, + }, + }, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Labels to apply to this membership.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The unique identifier of the membership.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceGKEHubMembershipCreate(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 := expandGKEHubMembershipDescription(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 := expandGKEHubMembershipLabels(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 + } + endpointProp, err := expandGKEHubMembershipEndpoint(d.Get("endpoint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("endpoint"); !isEmptyValue(reflect.ValueOf(endpointProp)) && (ok || !reflect.DeepEqual(v, endpointProp)) { + obj["endpoint"] = endpointProp + } + authorityProp, err := expandGKEHubMembershipAuthority(d.Get("authority"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("authority"); !isEmptyValue(reflect.ValueOf(authorityProp)) && (ok || !reflect.DeepEqual(v, authorityProp)) { + obj["authority"] = authorityProp + } + + url, err := replaceVars(d, config, "{{GKEHubBasePath}}projects/{{project}}/locations/global/memberships?membershipId={{membership_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Membership: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Membership: %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 Membership: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = gKEHubOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Membership", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create Membership: %s", err) + } + + if err := d.Set("name", flattenGKEHubMembershipName(opRes["name"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = replaceVars(d, config, "{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Membership %q: %#v", d.Id(), res) + + return resourceGKEHubMembershipRead(d, meta) +} + +func resourceGKEHubMembershipRead(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, "{{GKEHubBasePath}}{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Membership: %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("GKEHubMembership %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + + if err := d.Set("name", flattenGKEHubMembershipName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + if err := d.Set("description", flattenGKEHubMembershipDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + if err := d.Set("labels", flattenGKEHubMembershipLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + if err := d.Set("endpoint", flattenGKEHubMembershipEndpoint(res["endpoint"], d, config)); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + if err := d.Set("authority", flattenGKEHubMembershipAuthority(res["authority"], d, config)); err != nil { + return fmt.Errorf("Error reading Membership: %s", err) + } + + return nil +} + +func resourceGKEHubMembershipUpdate(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 Membership: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandGKEHubMembershipDescription(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 := expandGKEHubMembershipLabels(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 + } + authorityProp, err := expandGKEHubMembershipAuthority(d.Get("authority"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("authority"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, authorityProp)) { + obj["authority"] = authorityProp + } + + url, err := replaceVars(d, config, "{{GKEHubBasePath}}projects/{{project}}/locations/global/memberships/{{membership_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Membership %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("authority") { + updateMask = append(updateMask, "authority") + } + // 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 Membership %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Membership %q: %#v", d.Id(), res) + } + + err = gKEHubOperationWaitTime( + config, res, project, "Updating Membership", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceGKEHubMembershipRead(d, meta) +} + +func resourceGKEHubMembershipDelete(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 Membership: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{GKEHubBasePath}}{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Membership %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, "Membership") + } + + err = gKEHubOperationWaitTime( + config, res, project, "Deleting Membership", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Membership %q: %#v", d.Id(), res) + return nil +} + +func resourceGKEHubMembershipImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenGKEHubMembershipName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenGKEHubMembershipDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenGKEHubMembershipLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenGKEHubMembershipEndpoint(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["gke_cluster"] = + flattenGKEHubMembershipEndpointGkeCluster(original["gkeCluster"], d, config) + return []interface{}{transformed} +} +func flattenGKEHubMembershipEndpointGkeCluster(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["resource_link"] = + flattenGKEHubMembershipEndpointGkeClusterResourceLink(original["resourceLink"], d, config) + return []interface{}{transformed} +} +func flattenGKEHubMembershipEndpointGkeClusterResourceLink(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenGKEHubMembershipAuthority(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["issuer"] = + flattenGKEHubMembershipAuthorityIssuer(original["issuer"], d, config) + return []interface{}{transformed} +} +func flattenGKEHubMembershipAuthorityIssuer(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandGKEHubMembershipDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandGKEHubMembershipLabels(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 expandGKEHubMembershipEndpoint(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{}) + + transformedGkeCluster, err := expandGKEHubMembershipEndpointGkeCluster(original["gke_cluster"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGkeCluster); val.IsValid() && !isEmptyValue(val) { + transformed["gkeCluster"] = transformedGkeCluster + } + + return transformed, nil +} + +func expandGKEHubMembershipEndpointGkeCluster(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{}) + + transformedResourceLink, err := expandGKEHubMembershipEndpointGkeClusterResourceLink(original["resource_link"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResourceLink); val.IsValid() && !isEmptyValue(val) { + transformed["resourceLink"] = transformedResourceLink + } + + return transformed, nil +} + +func expandGKEHubMembershipEndpointGkeClusterResourceLink(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandGKEHubMembershipAuthority(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{}) + + transformedIssuer, err := expandGKEHubMembershipAuthorityIssuer(original["issuer"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIssuer); val.IsValid() && !isEmptyValue(val) { + transformed["issuer"] = transformedIssuer + } + + return transformed, nil +} + +func expandGKEHubMembershipAuthorityIssuer(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google-beta/resource_gke_hub_membership_generated_test.go b/google-beta/resource_gke_hub_membership_generated_test.go new file mode 100644 index 0000000000..1a1f53e3b6 --- /dev/null +++ b/google-beta/resource_gke_hub_membership_generated_test.go @@ -0,0 +1,152 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 TestAccGKEHubMembership_gkehubMembershipBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckGKEHubMembershipDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccGKEHubMembership_gkehubMembershipBasicExample(context), + }, + }, + }) +} + +func testAccGKEHubMembership_gkehubMembershipBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_container_cluster" "primary" { + name = "basiccluster%{random_suffix}" + location = "us-central1-a" + initial_node_count = 1 + provider = google-beta +} + +resource "google_gke_hub_membership" "membership" { + membership_id = "basic%{random_suffix}" + endpoint { + gke_cluster { + resource_link = "//container.googleapis.com/${google_container_cluster.primary.id}" + } + } + description = "test resource." + provider = google-beta +} +`, context) +} + +func TestAccGKEHubMembership_gkehubMembershipIssuerExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project": getTestProjectFromEnv(), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckGKEHubMembershipDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccGKEHubMembership_gkehubMembershipIssuerExample(context), + }, + }, + }) +} + +func testAccGKEHubMembership_gkehubMembershipIssuerExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_container_cluster" "primary" { + name = "basiccluster%{random_suffix}" + location = "us-central1-a" + initial_node_count = 1 + workload_identity_config { + identity_namespace = "%{project}.svc.id.goog" + } + provider = google-beta +} + +resource "google_gke_hub_membership" "membership" { + membership_id = "basic%{random_suffix}" + endpoint { + gke_cluster { + resource_link = "//container.googleapis.com/${google_container_cluster.primary.id}" + } + } + authority { + issuer = "https://container.googleapis.com/v1/${google_container_cluster.primary.id}" + } + description = "test resource." + provider = google-beta +} +`, context) +} + +func testAccCheckGKEHubMembershipDestroyProducer(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_gke_hub_membership" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{GKEHubBasePath}}{{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("GKEHubMembership still exists at %s", url) + } + } + + return nil + } +} diff --git a/website/docs/r/gke_hub_membership.html.markdown b/website/docs/r/gke_hub_membership.html.markdown new file mode 100644 index 0000000000..cd3cc80a97 --- /dev/null +++ b/website/docs/r/gke_hub_membership.html.markdown @@ -0,0 +1,188 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "GKEHub" +layout: "google" +page_title: "Google: google_gke_hub_membership" +sidebar_current: "docs-google-gke-hub-membership" +description: |- + Membership contains information about a member cluster. +--- + +# google\_gke\_hub\_membership + +Membership contains information about a member cluster. + +~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. +See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. + +To get more information about Membership, see: + +* [API documentation](https://cloud.google.com/gkehub/docs/reference/rest/v1beta1/projects.locations.memberships) +* How-to Guides + * [Registering a Cluster](https://cloud.google.com/anthos/multicluster-management/connect/registering-a-cluster#register_cluster) + +
+ + Open in Cloud Shell + +
+## Example Usage - Gkehub Membership Basic + + +```hcl +resource "google_container_cluster" "primary" { + name = "basiccluster" + location = "us-central1-a" + initial_node_count = 1 + provider = google-beta +} + +resource "google_gke_hub_membership" "membership" { + membership_id = "basic" + endpoint { + gke_cluster { + resource_link = "//container.googleapis.com/${google_container_cluster.primary.id}" + } + } + description = "test resource." + provider = google-beta +} +``` +
+ + Open in Cloud Shell + +
+## Example Usage - Gkehub Membership Issuer + + +```hcl +resource "google_container_cluster" "primary" { + name = "basiccluster" + location = "us-central1-a" + initial_node_count = 1 + workload_identity_config { + identity_namespace = "my-project-name.svc.id.goog" + } + provider = google-beta +} + +resource "google_gke_hub_membership" "membership" { + membership_id = "basic" + endpoint { + gke_cluster { + resource_link = "//container.googleapis.com/${google_container_cluster.primary.id}" + } + } + authority { + issuer = "https://container.googleapis.com/v1/${google_container_cluster.primary.id}" + } + description = "test resource." + provider = google-beta +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `membership_id` - + (Required) + The client-provided identifier of the membership. + + +- - - + + +* `description` - + (Optional) + The name of this entity type to be displayed on the console. + +* `labels` - + (Optional) + Labels to apply to this membership. + +* `endpoint` - + (Optional) + If this Membership is a Kubernetes API server hosted on GKE, this is a self link to its GCP resource. + Structure is documented below. + +* `authority` - + (Optional) + Authority encodes how Google will recognize identities from this Membership. + See the workload identity documentation for more details: + https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity + 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 `endpoint` block supports: + +* `gke_cluster` - + (Optional) + If this Membership is a Kubernetes API server hosted on GKE, this is a self link to its GCP resource. + Structure is documented below. + + +The `gke_cluster` block supports: + +* `resource_link` - + (Required) + Self-link of the GCP resource for the GKE cluster. + For example: `//container.googleapis.com/projects/my-project/zones/us-west1-a/clusters/my-cluster`. + It can be at the most 1000 characters in length. If the cluster is provisioned with Terraform, + this is `"//container.googleapis.com/${google_container_cluster.my-cluster.id}"`. + +The `authority` block supports: + +* `issuer` - + (Required) + A JSON Web Token (JWT) issuer URI. `issuer` must start with `https://` and // be a valid + with length <2000 characters. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `{{name}}` + +* `name` - + The unique identifier of the membership. + + +## 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 + + +Membership can be imported using any of these accepted formats: + +``` +$ terraform import google_gke_hub_membership.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 44a3ad8c97..0a78e06356 100644 --- a/website/google.erb +++ b/website/google.erb @@ -2327,6 +2327,22 @@ +
  • + GKEHub + +
  • +
  • Game Servers