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

Don't run sql precheck if projects.get call fails #4141

Merged
merged 3 commits into from
Oct 23, 2020
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 @@ -320,10 +320,12 @@ func retrieveServiceNetworkingNetworkName(d *schema.ResourceData, config *Config
if pid == "" {
return "", fmt.Errorf("Could not determine project")
}

log.Printf("[DEBUG] Retrieving project number by doing a GET with the project id, as required by service networking")
project, err := config.NewResourceManagerClient(userAgent).Projects.Get(pid).Do()
if err != nil {
return "", fmt.Errorf("Failed to retrieve project, pid: %s, err: %s", pid, err)
// note: returning a wrapped error is part of this method's contract!
// https://blog.golang.org/go1.13-errors
return "", fmt.Errorf("Failed to retrieve project, pid: %s, err: %w", pid, err)
}

networkName := networkFieldValue.Name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,24 +664,14 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error setting name: %s", err)
}

// Before we create the instance, check if at least 1 service connection exists for private SQL Instances.
// SQL Instances that fail to create are expensive- see https://github.com/hashicorp/terraform-provider-google/issues/7154
// We can fail fast to stop instance names from getting reserved.
network := d.Get("settings.0.ip_configuration.0.private_network").(string)
if network != "" {
// Borrow some of the functions from resource_service_networking_connection.go
serviceNetworkingNetworkName, err := retrieveServiceNetworkingNetworkName(d, config, network, userAgent)
err = sqlDatabaseInstanceServiceNetworkPrecheck(d, config, userAgent, network)
if err != nil {
return err
}
response, err := config.NewServiceNetworkingClient(userAgent).Services.Connections.List("services/servicenetworking.googleapis.com").
Network(serviceNetworkingNetworkName).Do()
if err != nil {
// It is possible that the identity creating the SQL Instance might not have permissions to call servicenetworking.services.connections.list
log.Printf("[WARNING] Failed to list Service Networking of the project. Skipping Service Networking check.")
} else {
if len(response.Connections) < 1 {
return fmt.Errorf("Error, failed to create instance because the network doesn't have at least 1 private services connection. Please see https://cloud.google.com/sql/docs/mysql/private-ip#network_requirements for how to create this connection.")
}
}
}

instance := &sqladmin.DatabaseInstance{
Expand Down Expand Up @@ -1324,3 +1314,32 @@ func sqlDatabaseIsMaster(d *schema.ResourceData) bool {
_, ok := d.GetOk("master_instance_name")
return !ok
}

func sqlDatabaseInstanceServiceNetworkPrecheck(d *schema.ResourceData, config *Config, userAgent, network string) error {
log.Printf("[DEBUG] checking network %q for at least one service networking connection", network)
// This call requires projects.get permissions, which may not have been granted to the Terraform actor,
// particularly in shared VPC setups. Most will! But it's not strictly required.
serviceNetworkingNetworkName, err := retrieveServiceNetworkingNetworkName(d, config, network, userAgent)
if err != nil {
var gerr *googleapi.Error
if errors.As(err, &gerr) {
log.Printf("[DEBUG] retrieved googleapi error while creating sn name for %q. precheck skipped. code %v and message: %s", network, gerr.Code, gerr.Body)
return nil
}

return err
}

response, err := config.NewServiceNetworkingClient(userAgent).Services.Connections.List("services/servicenetworking.googleapis.com").Network(serviceNetworkingNetworkName).Do()
if err != nil {
// It is possible that the actor creating the SQL Instance might not have permissions to call servicenetworking.services.connections.list
log.Printf("[WARNING] Failed to list Service Networking of the project. Skipped Service Networking precheck.")
return nil
}

if len(response.Connections) < 1 {
return fmt.Errorf("Error, failed to create instance because the network doesn't have at least 1 private services connection. Please see https://cloud.google.com/sql/docs/mysql/private-ip#network_requirements for how to create this connection.")
}

return nil
}