Fixes based on PR reviews
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Client Certificate Example
|
||||
|
||||
This folder contains an example of how to create client certificates for [Google Cloud SQL](https://cloud.google.com/sql/) database instance.
|
||||
This folder contains an example of how to create client certificates for [Cloud SQL](https://cloud.google.com/sql/) database instance.
|
||||
There can be only one pending operation at a given point of time because of the inherent Cloud SQL system architecture.
|
||||
This is a limitation on the concurrent writes to a CloudSQL database. To resolve this issue,
|
||||
This is a limitation on the concurrent writes to a Cloud SQL database. To resolve this issue,
|
||||
we will create the certificate in a separate module.
|
||||
|
||||
Creating the certificate while there are other operations ongoing will result in `googleapi: Error 409: Operation failed because another operation was already in progress.`
|
||||
|
||||
@@ -50,7 +50,7 @@ module "mysql" {
|
||||
engine = "${var.mysql_version}"
|
||||
machine_type = "${var.machine_type}"
|
||||
|
||||
master_zone = "a"
|
||||
master_zone = "${var.master_zone}"
|
||||
|
||||
# To make it easier to test this example, we are giving the servers public IP addresses and allowing inbound
|
||||
# connections from anywhere. In real-world usage, your servers should live in private subnets, only have private IP
|
||||
@@ -64,8 +64,9 @@ module "mysql" {
|
||||
},
|
||||
]
|
||||
|
||||
# Indicate that we want to create a failover replica
|
||||
enable_failover_replica = true
|
||||
failover_replica_zone = "b"
|
||||
failover_replica_zone = "${var.failover_replica_zone}"
|
||||
|
||||
# These together will construct the master_user privileges, i.e.
|
||||
# 'master_user_name'@'master_user_host' IDENTIFIED BY 'master_user_password'.
|
||||
|
||||
@@ -8,7 +8,15 @@ variable "project" {
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "The region to host the database in."
|
||||
description = "The region to host the database in (e.g. 'us-central1')."
|
||||
}
|
||||
|
||||
variable "master_zone" {
|
||||
description = "The preferred zone for the master instance (e.g. 'us-central1-a'). Must be different than 'failover_replica_zone'."
|
||||
}
|
||||
|
||||
variable "failover_replica_zone" {
|
||||
description = "The preferred zone for the failover instance (e.g. 'us-central1-b'). Must be different than 'master_zone'."
|
||||
}
|
||||
|
||||
# Note, after a name db instance is used, it cannot be reused for up to one week.
|
||||
|
||||
61
modules/mysql/compute_outputs.tf
Normal file
61
modules/mysql/compute_outputs.tf
Normal file
@@ -0,0 +1,61 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# SEPARATE TERRAFORM FILE TO COMPUTE OUTPUT VALUES AND KEEP THE MAIN MODULE CLEAN
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PREPARE LOCALS FOR THE OUTPUTS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
locals {
|
||||
# Replica proxy connection info
|
||||
failover_proxy_connection = "${join("",data.template_file.failover_proxy_connection.*.rendered)}"
|
||||
|
||||
# Replica certificate info
|
||||
failover_certificate = "${join("",data.template_file.failover_certificate.*.rendered)}"
|
||||
failover_certificate_common_name = "${join("",data.template_file.failover_certificate_common_name.*.rendered)}"
|
||||
failover_certificate_create_time = "${join("",data.template_file.failover_certificate_create_time.*.rendered)}"
|
||||
failover_certificate_expiration_time = "${join("",data.template_file.failover_certificate_expiration_time.*.rendered)}"
|
||||
failover_certificate_sha1_fingerprint = "${join("",data.template_file.failover_certificate_sha1_fingerprint.*.rendered)}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FAILOVER REPLICA PROXY CONNECTION TEMPLATE
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
data "template_file" "failover_proxy_connection" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${var.project}:${var.region}:${google_sql_database_instance.failover_replica.0.name}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FAILOVER REPLICA CERTIFICATE TEMPLATES
|
||||
#
|
||||
# We have to produce the certificate outputs via template_file. Using splat syntax would yield:
|
||||
# Resource 'google_sql_database_instance.failover_replica' does not have attribute 'server_ca_cert.0.cert'
|
||||
# for variable 'google_sql_database_instance.failover_replica.*.server_ca_cert.0.cert'
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
data "template_file" "failover_certificate" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.cert}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_common_name" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.common_name}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_create_time" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.create_time}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_expiration_time" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.expiration_time}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_sha1_fingerprint" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.sha1_fingerprint}"
|
||||
}
|
||||
@@ -15,9 +15,6 @@ locals {
|
||||
is_postgres = "${replace(var.engine, "POSTGRES", "") != var.engine}"
|
||||
is_mysql = "${replace(var.engine, "MYSQL", "") != var.engine}"
|
||||
|
||||
actual_master_zone = "${var.master_zone != "" ? format("%s-%s", var.region, var.master_zone) : ""}"
|
||||
actual_replica_zone = "${var.failover_replica_zone != "" ? format("%s-%s", var.region, var.failover_replica_zone) : ""}"
|
||||
|
||||
# Terraform does not allow using lists of maps with coditionals, so we have to
|
||||
# trick terraform by creating a string conditional first.
|
||||
# See https://github.com/hashicorp/terraform/issues/12453
|
||||
@@ -42,16 +39,6 @@ locals {
|
||||
# passing an empty string into 'private_network' causes
|
||||
# 'private_network" ("") doesn't match regexp "projects/...'
|
||||
ip_configuration = "${local.ip_configuration_def[local.ip_configuration_key]}"
|
||||
|
||||
# Replica proxy connection info
|
||||
failover_proxy_connection = "${join("",data.template_file.failover_proxy_connection.*.rendered)}"
|
||||
|
||||
# Replica certificate info
|
||||
failover_certificate = "${join("",data.template_file.failover_certificate.*.rendered)}"
|
||||
failover_certificate_common_name = "${join("",data.template_file.failover_certificate_common_name.*.rendered)}"
|
||||
failover_certificate_create_time = "${join("",data.template_file.failover_certificate_create_time.*.rendered)}"
|
||||
failover_certificate_expiration_time = "${join("",data.template_file.failover_certificate_expiration_time.*.rendered)}"
|
||||
failover_certificate_sha1_fingerprint = "${join("",data.template_file.failover_certificate_sha1_fingerprint.*.rendered)}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -78,7 +65,7 @@ resource "google_sql_database_instance" "master" {
|
||||
|
||||
location_preference {
|
||||
follow_gae_application = "${var.follow_gae_application}"
|
||||
zone = "${local.actual_master_zone}"
|
||||
zone = "${var.master_zone}"
|
||||
}
|
||||
|
||||
backup_configuration {
|
||||
@@ -146,14 +133,6 @@ resource "null_resource" "wait_for" {
|
||||
}
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# CREATE A TEMPLATE FILE TO SIGNAL ALL RESOURCES HAVE BEEN CREATED
|
||||
# ------------------------------------------------------------------------------
|
||||
data "template_file" "complete" {
|
||||
depends_on = ["google_sql_database_instance.failover_replica"]
|
||||
template = "true"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# CREATE THE FAILOVER REPLICA
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -188,7 +167,7 @@ resource "google_sql_database_instance" "failover_replica" {
|
||||
|
||||
location_preference {
|
||||
follow_gae_application = "${var.follow_gae_application}"
|
||||
zone = "${local.actual_replica_zone}"
|
||||
zone = "${var.failover_replica_zone}"
|
||||
}
|
||||
|
||||
disk_size = "${var.disk_size}"
|
||||
@@ -209,43 +188,9 @@ resource "google_sql_database_instance" "failover_replica" {
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FAILOVER REPLICA PROXY CONNECTION TEMPLATE
|
||||
# CREATE A TEMPLATE FILE TO SIGNAL ALL RESOURCES HAVE BEEN CREATED
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
data "template_file" "failover_proxy_connection" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${var.project}:${var.region}:${google_sql_database_instance.failover_replica.0.name}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FAILOVER REPLICA CERTIFICATE TEMPLATES
|
||||
#
|
||||
# We have to produce the certificate outputs via template_file. Using splat syntax would yield:
|
||||
# Resource 'google_sql_database_instance.failover_replica' does not have attribute 'server_ca_cert.0.cert'
|
||||
# for variable 'google_sql_database_instance.failover_replica.*.server_ca_cert.0.cert'
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
data "template_file" "failover_certificate" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.cert}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_common_name" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.common_name}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_create_time" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.create_time}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_expiration_time" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.expiration_time}"
|
||||
}
|
||||
|
||||
data "template_file" "failover_certificate_sha1_fingerprint" {
|
||||
count = "${var.enable_failover_replica}"
|
||||
template = "${google_sql_database_instance.failover_replica.0.server_ca_cert.0.sha1_fingerprint}"
|
||||
data "template_file" "complete" {
|
||||
depends_on = ["google_sql_database_instance.failover_replica"]
|
||||
template = "true"
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ variable "follow_gae_application" {
|
||||
}
|
||||
|
||||
variable "master_zone" {
|
||||
description = "Preferred zone for the instance (e.g. 'a'). Will be appended to the 'region'. Must be different than 'replica_zone'."
|
||||
description = "Preferred zone for the master instance (e.g. 'us-central1-a'). 'region'. If empty, Google will auto-assign a zone."
|
||||
default = ""
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ variable "enable_failover_replica" {
|
||||
}
|
||||
|
||||
variable "failover_replica_zone" {
|
||||
description = "The preferred zone for the failover instance (e.g. 'a'). Will be appended to the 'region'. Must be different than 'master_zone'."
|
||||
description = "The preferred zone for the failover instance (e.g. 'us-central1-b'). Must be different than 'master_zone'."
|
||||
default = ""
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestMySqlPrivateIP(t *testing.T) {
|
||||
test_structure.RunTestStage(t, "deploy", func() {
|
||||
region := test_structure.LoadString(t, exampleDir, KEY_REGION)
|
||||
projectId := test_structure.LoadString(t, exampleDir, KEY_PROJECT)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_PRIVATE)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_PRIVATE, "", "")
|
||||
test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions)
|
||||
|
||||
terraform.InitAndApply(t, terraformOptions)
|
||||
|
||||
@@ -65,7 +65,7 @@ func TestMySqlPublicIP(t *testing.T) {
|
||||
test_structure.RunTestStage(t, "deploy", func() {
|
||||
region := test_structure.LoadString(t, exampleDir, KEY_REGION)
|
||||
projectId := test_structure.LoadString(t, exampleDir, KEY_PROJECT)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_PUBLIC)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_PUBLIC, "", "")
|
||||
test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions)
|
||||
|
||||
terraform.InitAndApply(t, terraformOptions)
|
||||
|
||||
@@ -34,7 +34,11 @@ func TestMySqlReplicas(t *testing.T) {
|
||||
projectId := gcp.GetGoogleProjectIDFromEnvVar(t)
|
||||
region := getRandomRegion(t, projectId)
|
||||
|
||||
masterZone, replicaZone := getTwoDistinctRandomZonesForRegion(t, projectId, region)
|
||||
|
||||
test_structure.SaveString(t, exampleDir, KEY_REGION, region)
|
||||
test_structure.SaveString(t, exampleDir, KEY_MASTER_ZONE, masterZone)
|
||||
test_structure.SaveString(t, exampleDir, KEY_REPLICA_ZONE, replicaZone)
|
||||
test_structure.SaveString(t, exampleDir, KEY_PROJECT, projectId)
|
||||
})
|
||||
|
||||
@@ -48,7 +52,9 @@ func TestMySqlReplicas(t *testing.T) {
|
||||
test_structure.RunTestStage(t, "deploy", func() {
|
||||
region := test_structure.LoadString(t, exampleDir, KEY_REGION)
|
||||
projectId := test_structure.LoadString(t, exampleDir, KEY_PROJECT)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_REPLICAS)
|
||||
masterZone := test_structure.LoadString(t, exampleDir, KEY_MASTER_ZONE)
|
||||
replicaZone := test_structure.LoadString(t, exampleDir, KEY_REPLICA_ZONE)
|
||||
terraformOptions := createTerratestOptionsForMySql(projectId, region, exampleDir, NAME_PREFIX_REPLICAS, masterZone, replicaZone)
|
||||
test_structure.SaveTerraformOptions(t, exampleDir, terraformOptions)
|
||||
|
||||
terraform.InitAndApply(t, terraformOptions)
|
||||
@@ -91,8 +97,7 @@ func TestMySqlReplicas(t *testing.T) {
|
||||
|
||||
// Does not actually open up the connection - just returns a DB ref
|
||||
logger.Logf(t, "Connecting to: %s", publicIp)
|
||||
db, err := sql.Open("mysql",
|
||||
connectionString)
|
||||
db, err := sql.Open("mysql", connectionString)
|
||||
require.NoError(t, err, "Failed to open DB connection")
|
||||
|
||||
// Make sure we clean up properly
|
||||
|
||||
@@ -12,6 +12,8 @@ const DB_PASS = "testpassword"
|
||||
|
||||
const KEY_REGION = "region"
|
||||
const KEY_PROJECT = "project"
|
||||
const KEY_MASTER_ZONE = "masterZone"
|
||||
const KEY_REPLICA_ZONE = "replicaZone"
|
||||
|
||||
const MYSQL_VERSION = "MYSQL_5_7"
|
||||
|
||||
@@ -33,24 +35,39 @@ const MYSQL_EMPTY_TEST_TABLE_STATEMENT = "DELETE FROM test"
|
||||
const MYSQL_INSERT_TEST_ROW = "INSERT INTO test(name) VALUES(?)"
|
||||
|
||||
func getRandomRegion(t *testing.T, projectID string) string {
|
||||
//approvedRegions := []string{"europe-north1", "europe-west1", "europe-west2", "europe-west3", "us-central1", "us-east1", "us-west1"}
|
||||
approvedRegions := []string{"europe-north1"}
|
||||
approvedRegions := []string{"europe-north1", "europe-west1", "europe-west2", "europe-west3", "us-central1", "us-east1", "us-west1"}
|
||||
//approvedRegions := []string{"europe-north1"}
|
||||
return gcp.GetRandomRegion(t, projectID, approvedRegions, []string{})
|
||||
}
|
||||
|
||||
func createTerratestOptionsForMySql(projectId string, region string, exampleDir string, namePrefix string) *terraform.Options {
|
||||
func getTwoDistinctRandomZonesForRegion(t *testing.T, projectID string, region string) (string, string) {
|
||||
firstZone := gcp.GetRandomZoneForRegion(t, projectID, region)
|
||||
secondZone := gcp.GetRandomZoneForRegion(t, projectID, region)
|
||||
for {
|
||||
if firstZone != secondZone {
|
||||
break
|
||||
}
|
||||
secondZone = gcp.GetRandomZoneForRegion(t, projectID, region)
|
||||
}
|
||||
|
||||
return firstZone, secondZone
|
||||
}
|
||||
|
||||
func createTerratestOptionsForMySql(projectId string, region string, exampleDir string, namePrefix string, masterZone string, replicaZone string) *terraform.Options {
|
||||
|
||||
terratestOptions := &terraform.Options{
|
||||
// The path to where your Terraform code is located
|
||||
TerraformDir: exampleDir,
|
||||
Vars: map[string]interface{}{
|
||||
"region": region,
|
||||
"project": projectId,
|
||||
"name_prefix": namePrefix,
|
||||
"mysql_version": MYSQL_VERSION,
|
||||
"db_name": DB_NAME,
|
||||
"master_user_name": DB_USER,
|
||||
"master_user_password": DB_PASS,
|
||||
"region": region,
|
||||
"master_zone": masterZone,
|
||||
"failover_replica_zone": replicaZone,
|
||||
"project": projectId,
|
||||
"name_prefix": namePrefix,
|
||||
"mysql_version": MYSQL_VERSION,
|
||||
"db_name": DB_NAME,
|
||||
"master_user_name": DB_USER,
|
||||
"master_user_password": DB_PASS,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user