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

feat: support Cloud SQL\'s new field psc_auto_connections. #20307

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
3 changes: 3 additions & 0 deletions .changelog/12236.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
sql: added `psc_auto_connections` field to `instances` resource
```
53 changes: 53 additions & 0 deletions google/services/sql/resource_sql_database_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,25 @@ is set to true. Defaults to ZONAL.`,
Set: schema.HashString,
Description: `List of consumer projects that are allow-listed for PSC connections to this instance. This instance can be connected to with PSC from any network in these projects. Each consumer project in this list may be represented by a project number (numeric) or by a project id (alphanumeric).`,
},
"psc_auto_connections": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"consumer_service_project_id": {
Type: schema.TypeString,
Optional: true,
Description: `The project ID of consumer service project of this consumer endpoint.`,
},
"consumer_network": {
Type: schema.TypeString,
Required: true,
Description: `The consumer network of this consumer endpoint. This must be a resource path that includes both the host project and the network name. The consumer host project of this network might be different from the consumer service project.`,
},
},
},
Description: `A comma-separated list of networks or a comma-separated list of network-project pairs. Each project in this list is represented by a project number (numeric) or by a project ID (alphanumeric). This allows Private Service Connect connections to be created automatically for the specified networks.`,
},
},
},
},
Expand Down Expand Up @@ -1439,12 +1458,30 @@ func expandIpConfiguration(configured []interface{}, databaseVersion string) *sq
}
}

func expandPscAutoConnectionConfig(configured []interface{}) []*sqladmin.PscAutoConnectionConfig {
pscAutoConnections := make([]*sqladmin.PscAutoConnectionConfig, 0, len(configured))

for _, _flag := range configured {
if _flag == nil {
continue
}
_entry := _flag.(map[string]interface{})

pscAutoConnections = append(pscAutoConnections, &sqladmin.PscAutoConnectionConfig{
ConsumerNetwork: _entry["consumer_network"].(string),
ConsumerProject: _entry["consumer_service_project_id"].(string),
})
}
return pscAutoConnections
}

func expandPscConfig(configured []interface{}) *sqladmin.PscConfig {
for _, _pscConfig := range configured {
_entry := _pscConfig.(map[string]interface{})
return &sqladmin.PscConfig{
PscEnabled: _entry["psc_enabled"].(bool),
AllowedConsumerProjects: tpgresource.ConvertStringArr(_entry["allowed_consumer_projects"].(*schema.Set).List()),
PscAutoConnections: expandPscAutoConnectionConfig(_entry["psc_auto_connections"].([]interface{})),
}
}

Expand Down Expand Up @@ -2344,10 +2381,26 @@ func flattenIpConfiguration(ipConfiguration *sqladmin.IpConfiguration, d *schema
return []map[string]interface{}{data}
}

func flattenPscAutoConnections(pscAutoConnections []*sqladmin.PscAutoConnectionConfig) []map[string]interface{} {
flags := make([]map[string]interface{}, 0, len(pscAutoConnections))

for _, flag := range pscAutoConnections {
data := map[string]interface{}{
"consumer_network": flag.ConsumerNetwork,
"consumer_service_project_id": flag.ConsumerProject,
}

flags = append(flags, data)
}

return flags
}

func flattenPscConfigs(pscConfig *sqladmin.PscConfig) interface{} {
data := map[string]interface{}{
"psc_enabled": pscConfig.PscEnabled,
"allowed_consumer_projects": schema.NewSet(schema.HashString, tpgresource.ConvertStringArrToInterface(pscConfig.AllowedConsumerProjects)),
"psc_auto_connections": flattenPscAutoConnections(pscConfig.PscAutoConnections),
}

return []map[string]interface{}{data}
Expand Down
187 changes: 180 additions & 7 deletions google/services/sql/resource_sql_database_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -946,21 +946,78 @@ func TestAccSqlDatabaseInstance_withPSCEnabled_thenAddAllowedConsumerProjects_th
})
}

func TestAccSqlDatabaseInstance_basicInstance_thenPSCEnabled(t *testing.T) {
func TestAccSqlDatabaseInstance_withPSCEnabled_withoutPscAutoConnections(t *testing.T) {
t.Parallel()

instanceName := "tf-test-" + acctest.RandString(t, 10)
projectId := "psctestproject" + acctest.RandString(t, 10)
orgId := envvar.GetTestOrgFromEnv(t)
billingAccount := envvar.GetTestBillingAccountFromEnv(t)
projectId := envvar.GetTestProjectFromEnv()

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSqlDatabaseInstance_withPSCEnabled_withoutPscAutoConnections(instanceName),
Check: resource.ComposeTestCheckFunc(verifyPscAutoConnectionsOperation("google_sql_database_instance.instance", true, true, false, "", "")),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateIdPrefix: fmt.Sprintf("%s/", projectId),
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
},
})
}

func TestAccSqlDatabaseInstance_withPSCEnabled_withPscAutoConnections(t *testing.T) {
t.Parallel()

testId := "test-psc-auto-con" + acctest.RandString(t, 10)
instanceName := "tf-test-" + acctest.RandString(t, 10)
projectId := envvar.GetTestProjectFromEnv()
networkName := acctest.BootstrapSharedTestNetwork(t, testId)
network_short_link_name := fmt.Sprintf("projects/%s/global/networks/%s", projectId, networkName)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSqlDatabaseInstance_withPSCEnabled_withPscAutoConnections(instanceName, projectId, networkName),
Check: resource.ComposeTestCheckFunc(verifyPscAutoConnectionsOperation("google_sql_database_instance.instance", true, true, true, network_short_link_name, projectId)),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateIdPrefix: fmt.Sprintf("%s/", projectId),
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
},
})
}

func TestAccSqlDatabaseInstance_withPSCEnabled_thenAddPscAutoConnections_thenRemovePscAutoConnections(t *testing.T) {
t.Parallel()

testId := "test-psc-auto-con" + acctest.RandString(t, 10)
instanceName := "tf-test-" + acctest.RandString(t, 10)
projectId := envvar.GetTestProjectFromEnv()
networkName := acctest.BootstrapSharedTestNetwork(t, testId)
network_short_link_name := fmt.Sprintf("projects/%s/global/networks/%s", projectId, networkName)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSqlDatabaseInstance_basicInstanceForPsc(instanceName, projectId, orgId, billingAccount),
Config: testAccSqlDatabaseInstance_withPSCEnabled_withoutPscAutoConnections(instanceName),
Check: resource.ComposeTestCheckFunc(verifyPscAutoConnectionsOperation("google_sql_database_instance.instance", true, true, false, "", "")),
},
{
ResourceName: "google_sql_database_instance.instance",
Expand All @@ -970,8 +1027,19 @@ func TestAccSqlDatabaseInstance_basicInstance_thenPSCEnabled(t *testing.T) {
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
{
Config: testAccSqlDatabaseInstance_withPSCEnabled_withoutAllowedConsumerProjects(instanceName, projectId, orgId, billingAccount),
ExpectError: regexp.MustCompile("PSC connectivity can not be enabled"),
Config: testAccSqlDatabaseInstance_withPSCEnabled_withPscAutoConnections(instanceName, projectId, networkName),
Check: resource.ComposeTestCheckFunc(verifyPscAutoConnectionsOperation("google_sql_database_instance.instance", true, true, true, network_short_link_name, projectId)),
},
{
ResourceName: "google_sql_database_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateIdPrefix: fmt.Sprintf("%s/", projectId),
ImportStateVerifyIgnore: []string{"deletion_protection"},
},
{
Config: testAccSqlDatabaseInstance_withPSCEnabled_withoutPscAutoConnections(instanceName),
Check: resource.ComposeTestCheckFunc(verifyPscAutoConnectionsOperation("google_sql_database_instance.instance", true, true, false, "", "")),
},
},
})
Expand Down Expand Up @@ -3571,6 +3639,111 @@ func verifyPscOperation(resourceName string, isPscConfigExpected bool, expectedP
}
}

func verifyPscAutoConnectionsOperation(resourceName string, isPscConfigExpected bool, expectedPscEnabled bool, isPscAutoConnectionConfigExpected bool, expectedConsumerNetwork string, expectedConsumerProject string) func(*terraform.State) error {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Can't find %s in state", resourceName)
}

resourceAttributes := resource.Primary.Attributes
_, ok = resourceAttributes["settings.0.ip_configuration.#"]
if !ok {
return fmt.Errorf("settings.0.ip_configuration.# block is not present in state for %s", resourceName)
}

if isPscConfigExpected {
_, ok := resourceAttributes["settings.0.ip_configuration.0.psc_config.#"]
if !ok {
return fmt.Errorf("settings.0.ip_configuration.0.psc_config property is not present or set in state of %s", resourceName)
}

pscEnabledStr, ok := resourceAttributes["settings.0.ip_configuration.0.psc_config.0.psc_enabled"]
pscEnabled, err := strconv.ParseBool(pscEnabledStr)
if err != nil || pscEnabled != expectedPscEnabled {
return fmt.Errorf("settings.0.ip_configuration.0.psc_config.0.psc_enabled property value is not set as expected in state of %s, expected %v, actual %v", resourceName, expectedPscEnabled, pscEnabled)
}

_, ok = resourceAttributes["settings.0.ip_configuration.0.psc_config.0.psc_auto_connections.#"]
if !ok {
return fmt.Errorf("settings.0.ip_configuration.0.psc_config.0.psc_auto_connections property is not present or set in state of %s", resourceName)
}

if isPscAutoConnectionConfigExpected {
consumerNetwork, ok := resourceAttributes["settings.0.ip_configuration.0.psc_config.0.psc_auto_connections.0.consumer_network"]
if !ok || consumerNetwork != expectedConsumerNetwork {
return fmt.Errorf("settings.0.ip_configuration.0.psc_config.0.psc_auto_connections.0.consumer_network property is not present or set as expected in state of %s", resourceName)
}

consumerProject, ok := resourceAttributes["settings.0.ip_configuration.0.psc_config.0.psc_auto_connections.0.consumer_service_project_id"]
if !ok || consumerProject != expectedConsumerProject {
return fmt.Errorf("settings.0.ip_configuration.0.psc_config.0.psc_auto_connections.0.consumer_service_project_id property is not present or set as expected in state of %s", resourceName)
}
}
}

return nil
}
}

func testAccSqlDatabaseInstance_withPSCEnabled_withoutPscAutoConnections(instanceName string) string {
return fmt.Sprintf(`
resource "google_sql_database_instance" "instance" {
name = "%s"
region = "us-west2"
database_version = "MYSQL_8_0"
deletion_protection = false
settings {
tier = "db-g1-small"
ip_configuration {
psc_config {
psc_enabled = true
}
ipv4_enabled = false
}
backup_configuration {
enabled = true
binary_log_enabled = true
}
availability_type = "REGIONAL"
}
}
`, instanceName)
}

func testAccSqlDatabaseInstance_withPSCEnabled_withPscAutoConnections(instanceName string, projectId string, networkName string) string {
return fmt.Sprintf(`
data "google_compute_network" "testnetwork" {
name = "%s"
}

resource "google_sql_database_instance" "instance" {
name = "%s"
region = "us-west2"
database_version = "MYSQL_8_0"
deletion_protection = false
settings {
tier = "db-g1-small"
ip_configuration {
psc_config {
psc_enabled = true
psc_auto_connections {
consumer_network = "projects/%s/global/networks/%s"
consumer_service_project_id = "%s"
}
}
ipv4_enabled = false
}
backup_configuration {
enabled = true
binary_log_enabled = true
}
availability_type = "REGIONAL"
}
}
`, networkName, instanceName, projectId, networkName, projectId)
}

func testAccSqlDatabaseInstance_withPrivateNetwork_withoutAllocatedIpRange(databaseName, networkName string, specifyPrivatePathOption bool, enablePrivatePath bool) string {
privatePathOption := ""
if specifyPrivatePathOption {
Expand Down
34 changes: 34 additions & 0 deletions website/docs/r/sql_database_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,34 @@ resource "google_sql_database_instance" "main" {
}
```

### Cloud SQL Instance with PSC auto connections

```hcl
resource "google_sql_database_instance" "main" {
name = "psc-enabled-main-instance"
database_version = "MYSQL_8_0"
settings {
tier = "db-f1-micro"
ip_configuration {
psc_config {
psc_enabled = true
allowed_consumer_projects = ["allowed-consumer-project-name"]
psc_auto_connections {
consumer_network = "network-name"
consumer_service_project_id = "project-id"
}
}
ipv4_enabled = false
}
backup_configuration {
enabled = true
binary_log_enabled = true
}
availability_type = "REGIONAL"
}
}
```

## Argument Reference

The following arguments are supported:
Expand Down Expand Up @@ -404,6 +432,12 @@ The optional `settings.ip_configuration.psc_config` sublist supports:

* `allowed_consumer_projects` - (Optional) List of consumer projects that are allow-listed for PSC connections to this instance. This instance can be connected to with PSC from any network in these projects. Each consumer project in this list may be represented by a project number (numeric) or by a project id (alphanumeric).

* The optional `psc_config.psc_auto_connections` subblock - (Optional) A comma-separated list of networks or a comma-separated list of network-project pairs. Each project in this list is represented by a project number (numeric) or by a project ID (alphanumeric). This allows Private Service Connect connections to be created automatically for the specified networks.

* `consumer_network` - "The consumer network of this consumer endpoint. This must be a resource path that includes both the host project and the network name. For example, `projects/project1/global/networks/network1`. The consumer host project of this network might be different from the consumer service project."

* `consumer_service_project_id` - (Optional) The project ID of consumer service project of this consumer endpoint.

The optional `settings.location_preference` subblock supports:

* `follow_gae_application` - (Optional) A GAE application whose zone to remain
Expand Down