1
0
mirror of synced 2025-12-19 18:05:44 -05:00

Fixes based on PR reviews

This commit is contained in:
Petri Autero
2019-02-13 10:46:21 +02:00
parent 5976c2162f
commit f97a905959
10 changed files with 120 additions and 83 deletions

View File

@@ -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.`

View File

@@ -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'.

View File

@@ -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.

View 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}"
}

View File

@@ -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"
}

View File

@@ -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 = ""
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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,
},
}