Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport Add provider attribute universe_domain #1382

Merged
merged 1 commit into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ var OrgTargetEnvVars = []string{
"GOOGLE_ORG_2",
}

var UniverseDomainEnvVars = []string{
"GOOGLE_UNIVERSE_DOMAIN",
}

// This is the billing account that will be charged for the infrastructure used during testing. For
// that reason, it is also the billing account used for creating new projects.
var BillingAccountEnvVars = []string{
Expand Down Expand Up @@ -113,6 +117,12 @@ func GetTestCredsFromEnv() string {
return transport_tpg.MultiEnvSearch(CredsEnvVars)
}

// Returns googleapis.com if there's no universe set.
func GetTestUniverseDomainFromEnv(t *testing.T) string {
SkipIfEnvNotSet(t, IdentityUserEnvVars...)
return transport_tpg.MultiEnvSearch(UniverseDomainEnvVars)
}

// AccTestPreCheck ensures at least one of the region env variables is set.
func GetTestRegionFromEnv() string {
return transport_tpg.MultiEnvSearch(RegionEnvVars)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type ProviderModel struct {
UserProjectOverride types.Bool `tfsdk:"user_project_override"`
RequestTimeout types.String `tfsdk:"request_timeout"`
RequestReason types.String `tfsdk:"request_reason"`
UniverseDomain types.String `tfsdk:"universe_domain"`

// Generated Products
AccessApprovalCustomEndpoint types.String `tfsdk:"access_approval_custom_endpoint"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
"request_reason": schema.StringAttribute{
Optional: true,
},

"universe_domain": schema.StringAttribute{
Optional: true,
},
// Generated Products
"access_approval_custom_endpoint": &schema.StringAttribute{
Optional: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type FrameworkProviderConfig struct {
RequestBatcherServiceUsage *transport_tpg.RequestBatcher
Scopes types.List
TokenSource oauth2.TokenSource
UniverseDomain types.String
UserAgent string
UserProjectOverride types.Bool

Expand Down Expand Up @@ -337,6 +338,8 @@ func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context,
p.Zone = data.Zone
p.UserProjectOverride = data.UserProjectOverride
p.PollInterval = 10 * time.Second
p.Project = data.Project
p.UniverseDomain = data.UniverseDomain
p.RequestBatcherServiceUsage = transport_tpg.NewRequestBatcher("Service Usage", ctx, batchingConfig)
p.RequestBatcherIam = transport_tpg.NewRequestBatcher("IAM", ctx, batchingConfig)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ package provider

import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -208,6 +210,11 @@ func Provider() *schema.Provider {
Elem: &schema.Schema{Type: schema.TypeString},
},

"universe_domain": {
Type: schema.TypeString,
Optional: true,
},

"batching": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -1991,6 +1998,43 @@ func ProviderConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr
})
}

// set universe_domain based on the service account key file.
if config.Credentials != "" {
contents, _, err := verify.PathOrContents(config.Credentials)
if err != nil {
return nil, diag.FromErr(fmt.Errorf("error loading service account credentials: %s", err))
}
var content map[string]any

if err := json.Unmarshal([]byte(contents), &content); err != nil {
return nil, diag.FromErr(err)
}

if content["universe_domain"] != nil {
config.UniverseDomain = content["universe_domain"].(string)
}
}

// Check if the user provided a value from the universe_domain field
if v, ok := d.GetOk("universe_domain"); ok {
if config.UniverseDomain == "" {
config.UniverseDomain = v.(string)
} else if v.(string) != config.UniverseDomain {
if _, err := os.Stat(config.Credentials); err == nil {
return nil, diag.FromErr(fmt.Errorf("'%s' does not match the universe domain '%s' already set in the credential file '%s'. The 'universe_domain' provider configuration can not be used to override the universe domain that is defined in the active credential. Set the 'universe_domain' provider configuration when universe domain information is not already available in the credential, e.g. when authenticating with a JWT token.", v, config.UniverseDomain, config.Credentials))
} else {
return nil, diag.FromErr(fmt.Errorf("'%s' does not match the universe domain '%s' supplied directly to Terraform. The 'universe_domain' provider configuration can not be used to override the universe domain that is defined in the active credential. Set the 'universe_domain' provider configuration when universe domain information is not already available in the credential, e.g. when authenticating with a JWT token.", v, config.UniverseDomain))
}
}
}

// Replace hostname by the universe_domain field.
if config.UniverseDomain != "" && config.UniverseDomain != "googleapis.com" {
for key, basePath := range transport_tpg.DefaultBasePaths {
transport_tpg.DefaultBasePaths[key] = strings.ReplaceAll(basePath, "googleapis.com", config.UniverseDomain)
}
}

// Given that impersonate_service_account is a secondary auth method, it has
// no conflicts to worry about. We pull the env var in a DefaultFunc.
if v, ok := d.GetOk("impersonate_service_account"); ok {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package universe_test

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

func TestAccUniverseDomainDisk(t *testing.T) {
// Skip this test in all env since this can only run in specific test project.
t.Skip()

universeDomain := envvar.GetTestUniverseDomainFromEnv(t)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckComputeDiskDestroyProducer(t),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccUniverseDomain_basic_disk(universeDomain),
},
},
})
}

func TestAccDefaultUniverseDomainDisk(t *testing.T) {
universeDomain := "googleapis.com"

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckComputeDiskDestroyProducer(t),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccUniverseDomain_basic_disk(universeDomain),
},
},
})
}

func testAccUniverseDomain_basic_disk(universeDomain string) string {
return fmt.Sprintf(`
provider "google" {
universe_domain = "%s"
}

resource "google_compute_instance_template" "instance_template" {
name = "demo-this"
machine_type = "n1-standard-1"
// boot disk
disk {
disk_size_gb = 20
}
network_interface {
network = "default"
}
}
`, universeDomain)
}

func testAccCheckComputeDiskDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_disk" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := acctest.GoogleProviderConfig(t)

url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/zones/{{zone}}/disks/{{name}}")
if err != nil {
return err
}

billingProject := ""

if config.BillingProject != "" {
billingProject = config.BillingProject
}

_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: url,
UserAgent: config.UserAgent,
})
if err == nil {
return fmt.Errorf("ComputeDisk still exists at %s", url)
}
}

return nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package universe_test

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

func TestAccUniverseDomainPubSub(t *testing.T) {
// Skip this test in all env since this can only run in specific test project.
t.Skip()

universeDomain := envvar.GetTestUniverseDomainFromEnv(t)
topic := fmt.Sprintf("tf-test-topic-%s", acctest.RandString(t, 10))
subscription := fmt.Sprintf("tf-test-sub-%s", acctest.RandString(t, 10))

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckPubsubSubscriptionDestroyProducer(t),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccUniverseDomain_basic_pubsub(universeDomain, topic, subscription),
},
},
})
}

func testAccUniverseDomain_basic_pubsub(universeDomain, topic, subscription string) string {
return fmt.Sprintf(`
provider "google" {
universe_domain = "%s"
}

resource "google_pubsub_topic" "foo" {
name = "%s"
}

resource "google_pubsub_subscription" "foo" {
name = "%s"
topic = google_pubsub_topic.foo.id

message_retention_duration = "1200s"
retain_acked_messages = true
ack_deadline_seconds = 20
expiration_policy {
ttl = ""
}
enable_message_ordering = false
}
`, universeDomain, topic, subscription)
}

func testAccCheckPubsubSubscriptionDestroyProducer(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_pubsub_subscription" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := acctest.GoogleProviderConfig(t)

url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{PubsubBasePath}}projects/{{project}}/subscriptions/{{name}}")
if err != nil {
return err
}

billingProject := ""

if config.BillingProject != "" {
billingProject = config.BillingProject
}

_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: url,
UserAgent: config.UserAgent,
})
if err == nil {
return fmt.Errorf("PubsubSubscription still exists at %s", url)
}
}

return nil
}
}
Loading
Loading