Creating The Database
The next step in the Hands on Guide to Google Cloud says we should connect the Etherpad instance to a database. Let’s see how we create the database using the automated processes we are developing.
Creating the database using Terraform
Cloud SQL module
A SQL instance was created by the terraform code that was generated from the boilerplate. University members can see the sql.tf template. It contains the following:
|
|
The University has decided to standardise on Postgres for new database instances. The comment helpfully links to the documentation for the Google SQL module.
This documentation has a handy box with provision instructions:
The required variables are:
Variable | Notes |
---|---|
database_version | The description unhelpfully says The database version to use. Presumably somewhere under the covers, Terraform will call the Cloud SDK, so we can use that documentation to find out which database versions we can use. My colleague picked “POSTGRES_11”. |
name | The Hands On Guide says to use sql-{random string}. Looking at the terraform code above we can see it creates a random_id called sql_instance_name which is used to name the database. Because Terraform is really clever it doesn’t give it a new random ID every time it is run! |
project_id | This is the name of the project which was created for me and described in the Hands On Guide under Create a Google Project |
zone | The Hands On Guide says For Region choose europe-west2 (London) and keep Zone to Any. According to the Google Cloud documentation a Region is a collection of zones. Scrolling down it lists all the zones that can be picked for each type of resource. It seems we have to pick the whole zone in terraform rather than just the region. |
Hang on - that’s only 4! I am sure they said there were 5 required variables.
Finding the Project ID
The way the project is defined is interesting. Since all resources are grouped into a project,
this needs to be defined so all resources can use the same project name.
A local value in Terraform is s special type of variable.
It is defined in a locals
block, and is referenced in expressions as local.<NAME>
.
In my project there is a locals.tf
(The
cookiecutter template
is accessible to University staff). It contains the following:
The project id is defined here, but takes it’s value from the project module. This is defined in main.tf:
|
|
The project module is publicly available. We can see in
outputs.tf
that project_id is defined as an output, which is why it can be read as per the snippet of locals.tf
from my project which is pasted above. We can see that
the project_id is created from a
random_id
called project_name, which is defined above the project. The random id
assigns a prefix from local.slug
which is defined as a string in the project, and
local.short_workspace
. This is defined in locals.tf in my project as follows:
A note about Workspaces
A terraform workspace is an instance of the application. The boilerplate deals with development, staging and production, but others can also be catered for. The current workspace can be discovered using
The development
workspace is the default workspace for
Logan.
The terraform default workspace is
default
.
So putting what we discovered above together, my project ID is localslug-devel-randomid
More Parameters to the SQL module
There are a couple more parameters to the cloud SQL module which would be useful to set rather than allow to default:
Variable | Notes |
---|---|
db_name | A name for the database. This is required for the application to connect. The Hands On Guide says to choose etherpad |
user_name | This is the admin user for the database. This confused me for a while, because it isn’t how the application user connects. |
So that leaves me with something like this in my projects sql.tf
.
|
|
The local variables are defined in locals.tf
in my project.
So all this is very nice, but how do I make the application connect to the database?
Database Account
The application needs a user in the database. We don’t use the admin user because this
will likely have more permissions than the application needs. This users name is assigned in
locals.tf
to a variable webapp_sql_user
.
The user itself is created in webapp.tf
using module
google_sql_user.
First the password is created in a similar manner as the random string part of the project id.
A random_password resource
is created and called webapp_sql_user_password
.
Next the google_sql_user resource
is used to create a user in the database. The username is set from the local value mentioned above. It takes parameters
of the username, defined in locals (as mentioned above), the password that was just created, and the SQL instance which
was created above. The
Google sql-db module documents
instance_name
as one of the outputs from that module, so we can
use that as an input to specify the database to create the user in.
Service Account
As the Hands On Guide notes, even though we have just created the database and a user, the application still needs permission to connect to it.
There are a couple of service accounts defined in the terraform code. One in main.tf is used in setting up the project and so has
more privileges. We actually want to grant access to the service account that gets created to run the project. It’s
a bit like the user an application runs under in Unix. As we saw above the project is
created by our gcp-cloud-run-app
terraform module. In
outputs.tf
we can see the service_account
output is defined to return google_service_account.webapp
. This is defined in
main.tf
as
This is a terraform resource
of type google_service_account
The
Google Service Account Documentation
notes that this exports some attributes including the email address of the service account. This email address is used to
identify the account when granting access in main.tf
as follows:
|
|
This is actually quite clever. This makes use of the count meta argument to effectively construct an if statement. It is saying create zero resources (i.e. loop 0 times) if the sql connection is blank, but one (loop 1 time) if the connection name is set. If there is a database, obviously the application will need to connect to it!