Skip to content

Commit

Permalink
Don't run sql precheck if projects.get call fails (#4141)
Browse files Browse the repository at this point in the history
  • Loading branch information
rileykarson authored Oct 23, 2020
1 parent 001a074 commit fae92c2
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
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
}

0 comments on commit fae92c2

Please sign in to comment.