Creating Google CloudSQL Instances manually using kubectl (experimental)

Note

: This document is for users who are looking to understand the underlying APIs involved in making a bindable service instance using SQLInstance, SQLDatabase, SQLUser and SecretTemplate resources. For a simpler user experience, the alternative Creating an CloudSQL service instance through a Carvel Package topic is recommended.

Prerequisite

Meet the prerequisites and keep the following information to hand:

Create a CloudSQL service instance by using kubectl

At a minimum, a useable database instance consists of a SQLInstance, a SQLDatabase, and a SQLUser.

Realistically, in addition to that we will also want another set of Secrets:

  • one Secret per SQLInstance to hold the password for the instance’s admin role
  • one Secret per SQLUser to hold that user’s password

In the simplest case, with one SQLInstance, one SQLDatabase, and one SQLUser, we need to manage the following set of interrelated resources:

Dependencies between objects of a usable Cloud SQL database instance

Create the Secrets for the Database admin & user

First we need to ensure that the Secrets which hold the admin’s and user’s password exist, so we can reference them in the SQLInstance and SQLUser objects.

Those secrets can be created by any means. In this guide will leverage the Password API from Carvel’s secretgen controller, which will create the Secrets for us. However, any other mechanism to manage those secrets works too.

kind: List
apiVersion: v1
items:
- kind: Password
  apiVersion: secretgen.k14s.io/v1alpha1
  metadata:
    name: sql-admin-creds
    namespace: service-instances
  spec: &passwordSpec
    length: 64
    secretTemplate:
      type: Opaque
      stringData:
        password: $(value)
- kind: Password
  apiVersion: secretgen.k14s.io/v1alpha1
  metadata:
    name: sql-user-creds
    namespace: service-instances
  spec: *passwordSpec

Applying this will create two Passwords which in turn will have two Secrets created:

kubectl -n service-instances get passwords,secrets sql-user-creds sql-admin-creds
NAME                                         DESCRIPTION           AGE
password.secretgen.k14s.io/sql-user-creds    Reconcile succeeded   4m41s
password.secretgen.k14s.io/sql-admin-creds   Reconcile succeeded   4m41s

NAME                     TYPE     DATA   AGE
secret/sql-user-creds    Opaque   1      4m41s
secret/sql-admin-creds   Opaque   1      4m41s

Create a usable postgres database

Now we can reference those two secrets and use the Config Connector APIs to create our database objects:

Note

: You need to allow access from the Kubernetes cluster’s NAT IP. You can get the NAT IP via the command described in the prerequisites. This NAT IP then needs to be used in the SQLInstance’s spec.settings.ipConfiguration.authorizedNetworks.

apiVersion: sql.cnrm.cloud.google.com/v1beta1
kind: SQLInstance
metadata:
  name: sql-instance
  namespace: service-instances
spec:
  databaseVersion: POSTGRES_14
  #! If you have deployed your cluster into a different region, you might want
  #! to change this and deploy the SQLInstance into the same region as the
  #! cluster, to avoid traffic going across regions.
  region: europe-west6
  rootPassword:
    valueFrom:
      secretKeyRef:
        key: password
        name: sql-admin-creds
  settings:
    tier: db-g1-small
    ipConfiguration:
      authorizedNetworks:
      - name: cluster-NAT-IP
        #! Update this value with your NAT IP address in CIDR notation (e.g. 8.8.8.8/32). See above.
        value: <NAT-IP>
      ipv4Enabled: true
---
apiVersion: sql.cnrm.cloud.google.com/v1beta1
kind: SQLDatabase
metadata:
  name: sql-database
  namespace: service-instances
spec:
  charset: UTF8
  collation: en_US.UTF8
  instanceRef:
    name: sql-instance
---
apiVersion: sql.cnrm.cloud.google.com/v1beta1
kind: SQLUser
metadata:
  name: sql-user
  namespace: service-instances
spec:
  instanceRef:
    name: sql-instance
  password:
    valueFrom:
      secretKeyRef:
        key: password
        name: sql-user-creds

Once those objects are committed to the Kubernetes API, the Config Connector will cause the creation of those resources on GCP. This will take a short amount of time.

The three resources report their status and potential problems/errors back. If all goes well we should see all of those resources as “Ready” & “UpToDate” after a couple of minutes.

# kubectl -n service-instances get sqlinstance,sqldatabase,sqluser
NAME                                                 AGE     READY   STATUS     STATUS AGE
sqlinstance.sql.cnrm.cloud.google.com/sql-instance   3d20h   True    UpToDate   3d20h

NAME                                                 AGE     READY   STATUS     STATUS AGE
sqldatabase.sql.cnrm.cloud.google.com/sql-database   3d20h   True    UpToDate   3d20h

NAME                                         AGE     READY   STATUS     STATUS AGE
sqluser.sql.cnrm.cloud.google.com/sql-user   3d20h   True    UpToDate   3d20h

You can also see this Cloud SQL instance in the Google Cloud Console.

Note

: Cloud SQL does not allow you to reuse the name of a deleted instance for a week. If you try to create a new SQLInstance with a name you have already used previously, you will see an error like

Note

[…] When you delete an instance, you can’t reuse the name of the deleted instance until one week from the deletion date. […]

You can use a different name for the SQLInstance; make sure to use replace that name in all examples going forward.

Create a Binding Specification compatible Secret for the database

As pointed out, none of the created objects are compatible with the Service Binding Specification. To help with that, we can create a secret which holds the data we need to know to connect to and use the Cloud SQL instance and which allows the platform to discover the fact that this instance can be “claimed” and “bound” to application workloads.

For this to be an automated process, we can use the SecretTemplate API of the secretgen controller. The secretgen controller needs to be able to read the resources created, thus we also need to deploy some RBAC rules to allow for that:

apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretTemplate
metadata:
  name: sql-instance-claimable
  namespace: service-instances
spec:
  inputResources:
  - name: sqlInstance
    ref:
      apiVersion: sql.cnrm.cloud.google.com/v1beta1
      kind: SQLInstance
      name: sql-instance
  - name: sqlDatabase
    ref:
      apiVersion: sql.cnrm.cloud.google.com/v1beta1
      kind: SQLDatabase
      name: sql-database
  - name: sqlUser
    ref:
      apiVersion: sql.cnrm.cloud.google.com/v1beta1
      kind: SQLUser
      name: sql-user
  - name: sqlUserSecret
    ref:
      apiVersion: v1
      kind: Secret
      name: $(.sqlUser.spec.password.valueFrom.secretKeyRef.name)
  serviceAccountName: sql-objects-reader
  template:
    data:
      password: $(.sqlUserSecret.data.password)
    metadata:
      labels:
        app.kubernetes.io/component: cloudsql-postgres
        app.kubernetes.io/instance: "$(.sqlInstance.metadata.name)"
        services.apps.tanzu.vmware.com/class: cloudsql-postgres
    stringData:
      database: $(.sqlDatabase.metadata.name)
      host: $(.sqlInstance.status.publicIpAddress)
      port: "5432"
      type: postgresql
      username: $(.sqlUser.metadata.name)
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sql-objects-reader
  namespace: service-instances
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sql-objects-reader
  namespace: service-instances
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: sql-objects-reader
subjects:
- kind: ServiceAccount
  name: sql-objects-reader
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: sql-objects-reader
  namespace: service-instances
rules:
- apiGroups:     [ "" ]
  resources:     [ "secrets" ]
  verbs:         &objReaderVerbs [ "get", "list", "watch" ]
  resourceNames: [ "sql-user-creds", "sql-admin-creds" ]
- apiGroups:     [ "sql.cnrm.cloud.google.com" ]
  resources:     [ "sqlinstances", "sqldatabases", "sqlusers" ]
  verbs:         *objReaderVerbs
  resourceNames: [ "sql-instance", "sql-database", "sql-user" ]

Verify

Find the name of the secret produced by reading the status of SecretTemplate. To do so, run:

kubectl get secrettemplate -n service-instances sql-instance-claimable -o jsonpath="{.status.secret.name}"

Delete a CloudSQL service instance

Delete an CloudSQL service instance and all additional and related objects by running:

kubectl -n service-instances delete \
  sqlinstance/sql-instance \
  sqldatabase/sql-database \
  sqluser/sql-user \
  secrettemplate/sql-instance-claimable \
  password/sql-admin-creds \
  password/sql-user-creds \
  serviceaccount/sql-objects-reader \
  rolebinding/sql-objects-reader \
  roles/sql-objects-reader

Summary and Next Steps

You have learned how to use Carvel’s SecretTemplate API to construct a secret that is compatible with the binding specification in order to create an Google CloudSQL service instance.

Now that you have this available in the cluster, you can learn how to make use of it by continuing where you left off in Consuming Google Cloud SQL on Tanzu Application Platform (TAP) with Config Connector.

check-circle-line exclamation-circle-line close-line
Scroll to top icon