Skip to content

Commit 0543d77

Browse files
Mirror from monorepo 4bafe172 (#694)
Automated mirror from monorepo commit `4bafe1726b8a45018bef789ae5d25b57124c3cc0`. This PR was automatically generated by the terraform provider mirroring workflow. Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 90223b6 commit 0543d77

File tree

12 files changed

+567
-11
lines changed

12 files changed

+567
-11
lines changed

examples/deployments/gcp/gke-bigquery-connector/README.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,22 @@ terraform apply
5858

5959
You should now be able to connect to BigQuery using the Connector.
6060

61-
For example, using the `bq` CLI:
62-
63-
```bash
64-
CONNECTOR_IP=$(terraform output -raw kubernetes_service_external_ip)
65-
bq query --api http://${CONNECTOR_IP}:7777 'SELECT 1'
66-
```
67-
68-
Or from within your GKE cluster, using the internal hostname:
61+
By default, the Connector is deployed with an internal load balancer, which is only accessible from within the VPC. You can connect from within your GKE cluster using the internal hostname:
6962

7063
```bash
7164
CONNECTOR_HOSTNAME=$(terraform output -raw kubernetes_service_internal_hostname)
7265
bq query --api http://${CONNECTOR_HOSTNAME}:7777 'SELECT 1'
7366
```
7467

75-
Or using port forward:
68+
For local development or testing, you can use port forwarding:
7669

7770
```bash
7871
kubectl port-forward services/formal-connector 7777
7972
bq query --api http://localhost:7777 'SELECT 1'
8073
```
8174

75+
If you need external access from outside the VPC, you can modify the service configuration in `helm/values.yaml` to use an external load balancer by removing the `cloud.google.com/load-balancer-type` annotation.
76+
8277
> 💡 **Note:** If you don't want Terraform to manage the Connector deployment in your GKE cluster, you can remove the `helm_release` resource from `main.tf`. You will need to run Terraform first, then Helm configured with the appropriate values.
8378
8479

examples/deployments/gcp/gke-bigquery-connector/outputs.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
output "kubernetes_service_external_ip" {
2-
description = "External IP address of the connector"
1+
output "kubernetes_service_ip" {
2+
description = "Load balancer IP address of the connector (internal by default)"
33
value = data.kubernetes_service.formal_connector.status[0].load_balancer[0].ingress[0].ip
44
}
55

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Formal Connector GKE / Cloud SQL Setup
2+
3+
This directory contains a demonstration of how to set up a Formal Connector in GKE, connecting to Cloud SQL for PostgreSQL using the Cloud SQL Auth Proxy with IAM authentication.
4+
5+
It contains:
6+
* A Terraform configuration to set up the required GCP and Formal resources
7+
* Deployment of the official Formal Helm charts (`formal/connector` and `formal/ecr-cred`)
8+
* Cloud SQL Proxy configured as a sidecar container for secure connectivity
9+
* GCP IAM authentication handled by the Formal Connector
10+
11+
You can use it as-is to set up a Formal Connector in your GKE cluster, or as a starting point to integrate the Connector in your existing deployment pipeline.
12+
13+
Formal general purpose documentation is available at [docs.joinformal.com](https://docs.joinformal.com).
14+
15+
16+
## Architecture
17+
18+
```
19+
┌─────────────────────────────────────────────────────────────────┐
20+
│ GKE Pod │
21+
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
22+
│ │ Formal Connector │ │ Cloud SQL Auth Proxy │ │
23+
│ │ │ │ │ │
24+
│ │ Listens on :5432 │─────▶│ Connects to Cloud SQL via │ │
25+
│ │ (client-facing) │ │ localhost:5433 │ │
26+
│ │ (IAM auth via WI) │ │ │ │
27+
│ └─────────────────────┘ └─────────────────────────────┘ │
28+
└─────────────────────────────────────────────────────────────────┘
29+
30+
31+
┌─────────────────────┐
32+
│ Cloud SQL │
33+
│ (PostgreSQL) │
34+
└─────────────────────┘
35+
```
36+
37+
The Cloud SQL Proxy runs as a sidecar and handles secure connectivity to Cloud SQL.
38+
The Formal Connector handles GCP IAM authentication using Workload Identity to generate IAM tokens.
39+
40+
41+
## Prerequisites
42+
43+
On your side, you need:
44+
* Helm, Terraform and `gcloud` CLI installed.
45+
* `gcloud` CLI configured and authenticated. You (or your Terraform runner) need to have write access to both the GCP project and the GKE cluster.
46+
* Cloud SQL Admin API enabled (`gcloud services enable sqladmin.googleapis.com`).
47+
* A GKE cluster with Workload Identity enabled.
48+
* A Cloud SQL for PostgreSQL instance with IAM database authentication enabled.
49+
50+
On the Formal side, you need:
51+
* A Formal API key, that you can create in the Formal Console.
52+
* Formal ECR credentials (ask your Formal contact for access).
53+
54+
55+
## Setup
56+
57+
1. Create a `terraform.tfvars` file:
58+
```hcl
59+
# Required variables
60+
project_id = "your-project-id"
61+
region = "your-region"
62+
cluster_name = "your-cluster-name"
63+
cloud_sql_instance_connection = "your-project:your-region:your-instance"
64+
formal_api_key = "your-formal-api-key"
65+
ecr_access_key_id = "your-ecr-access-key-id"
66+
ecr_secret_access_key = "your-ecr-secret-access-key"
67+
68+
# Optional variables
69+
namespace = "default"
70+
connector_name = "cloudsql-connector"
71+
postgres_port = 5432
72+
```
73+
74+
2. Initialize and apply the Terraform configuration:
75+
```bash
76+
terraform init
77+
terraform apply
78+
```
79+
80+
The Terraform configuration automatically creates:
81+
* A GCP service account with the necessary IAM roles
82+
* Workload Identity binding for the Kubernetes service account
83+
* The Cloud SQL IAM database user
84+
85+
You should now be able to connect to Cloud SQL via the Connector.
86+
87+
By default, the Connector is deployed with an internal load balancer, which is only accessible from within the VPC. You can connect from within your GKE cluster using the internal hostname:
88+
89+
```bash
90+
CONNECTOR_HOSTNAME=$(terraform output -raw kubernetes_service_internal_hostname)
91+
psql "host=${CONNECTOR_HOSTNAME} port=5432 user=<formal-user> dbname=<database>@cloudsql-connector-postgres"
92+
```
93+
94+
For local development or testing, you can use port forwarding:
95+
96+
```bash
97+
kubectl port-forward services/formal-connector 5432
98+
psql "host=localhost port=5432 user=<formal-user> dbname=<database>@cloudsql-connector-postgres"
99+
```
100+
101+
If you need external access from outside the VPC, you can modify the service configuration in `main.tf` to use an external load balancer by removing the `cloud.google.com/load-balancer-type` annotation.
102+
103+
> **Note:** If you don't want Terraform to manage the Connector deployment in your GKE cluster, you can remove the `helm_release` resources from `main.tf`. You will need to run Terraform first, then Helm configured with the appropriate values.
104+
105+
106+
## Customization
107+
108+
### Using Private IP
109+
110+
By default, this example uses Cloud SQL's public IP. If your Cloud SQL instance has a private IP and your GKE cluster can reach it (e.g., same VPC or VPC peering), add the `--private-ip` flag to the `sidecars` configuration in `main.tf`:
111+
112+
```hcl
113+
args = [
114+
var.cloud_sql_instance_connection,
115+
"--port=5433",
116+
"--private-ip"
117+
]
118+
```
119+
120+
### Adjusting Resources
121+
122+
You can adjust the Connector and Cloud SQL Proxy resources by modifying the `values` block in `main.tf`.
123+
124+
125+
## Troubleshooting
126+
127+
If you encounter issues, here are a few things you can check:
128+
129+
* Check the Connector pod status for any issues (e.g. `kubectl describe pod ...`)
130+
* Check the logs of both containers:
131+
* Connector logs: `kubectl logs <pod-name> -c connector`
132+
* Cloud SQL Proxy logs: `kubectl logs <pod-name> -c cloud-sql-proxy`
133+
* Check Kubernetes events (e.g. `kubectl get events`)
134+
* Verify the GCP service account has the correct IAM roles:
135+
* `roles/cloudsql.client`
136+
* `roles/cloudsql.instanceUser`
137+
* Verify the Workload Identity binding is correctly configured
138+
139+
If you still encounter issues, please reach out to us!
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
google = {
6+
source = "hashicorp/google"
7+
version = ">= 4.0"
8+
}
9+
kubernetes = {
10+
source = "hashicorp/kubernetes"
11+
version = ">= 2.0"
12+
}
13+
helm = {
14+
source = "hashicorp/helm"
15+
version = ">= 2.0"
16+
}
17+
}
18+
}
19+
20+
provider "google" {
21+
project = var.project_id
22+
region = var.region
23+
}
24+
25+
data "google_client_config" "default" {}
26+
27+
provider "kubernetes" {
28+
host = "https://${data.google_container_cluster.cluster.endpoint}"
29+
token = data.google_client_config.default.access_token
30+
cluster_ca_certificate = base64decode(data.google_container_cluster.cluster.master_auth[0].cluster_ca_certificate)
31+
}
32+
33+
provider "helm" {
34+
kubernetes = {
35+
host = "https://${data.google_container_cluster.cluster.endpoint}"
36+
token = data.google_client_config.default.access_token
37+
cluster_ca_certificate = base64decode(data.google_container_cluster.cluster.master_auth[0].cluster_ca_certificate)
38+
}
39+
}
40+
41+
data "google_container_cluster" "cluster" {
42+
name = var.cluster_name
43+
location = var.region
44+
project = var.project_id
45+
}
46+
47+
module "wif" {
48+
source = "./modules/wif"
49+
50+
project_id = var.project_id
51+
cluster_name = var.cluster_name
52+
region = var.region
53+
namespace = var.namespace
54+
cloud_sql_instance_connection = var.cloud_sql_instance_connection
55+
}
56+
57+
module "formal" {
58+
source = "./modules/formal"
59+
60+
name = var.connector_name
61+
formal_api_key = var.formal_api_key
62+
postgres_port = var.postgres_port
63+
gcp_service_account_email = module.wif.service_account_email
64+
}
65+
66+
# ECR credentials job for pulling Formal Connector image from ECR
67+
resource "helm_release" "ecr_cred" {
68+
name = "formal-ecr-cred"
69+
repository = "https://formalco.github.io/helm-charts"
70+
chart = "ecr-cred"
71+
version = "0.3.0"
72+
namespace = var.namespace
73+
74+
values = [yamlencode({
75+
ecrAccessKeyId = var.ecr_access_key_id
76+
ecrSecretAccessKey = var.ecr_secret_access_key
77+
})]
78+
}
79+
80+
resource "helm_release" "formal_connector" {
81+
name = "formal-connector"
82+
repository = "https://formalco.github.io/helm-charts"
83+
chart = "connector"
84+
version = "0.11.0"
85+
namespace = var.namespace
86+
87+
values = [yamlencode({
88+
formalAPIKey = module.formal.connector_api_key
89+
pullWithCredentials = true
90+
91+
ports = [
92+
{
93+
name = "postgres"
94+
port = var.postgres_port
95+
}
96+
]
97+
98+
serviceAccount = {
99+
create = true
100+
name = "formal-connector"
101+
annotations = {
102+
"iam.gke.io/gcp-service-account" = module.wif.service_account_email
103+
}
104+
}
105+
106+
service = {
107+
type = "LoadBalancer"
108+
annotations = {
109+
"cloud.google.com/load-balancer-type" = "Internal"
110+
}
111+
}
112+
113+
# Cloud SQL Proxy sidecar for secure connectivity to Cloud SQL
114+
# Uses port 5433 internally to avoid conflict with connector's port 5432
115+
# Note: Do not use --auto-iam-authn - the Formal Connector handles IAM auth via iam_gcp
116+
sidecars = [
117+
{
118+
name = "cloud-sql-proxy"
119+
image = "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.2"
120+
args = [
121+
var.cloud_sql_instance_connection,
122+
"--port=5433"
123+
]
124+
securityContext = {
125+
runAsNonRoot = true
126+
}
127+
resources = {
128+
requests = {
129+
cpu = "100m"
130+
memory = "128Mi"
131+
}
132+
limits = {
133+
cpu = "500m"
134+
memory = "256Mi"
135+
}
136+
}
137+
}
138+
]
139+
})]
140+
141+
depends_on = [module.wif, helm_release.ecr_cred]
142+
}
143+
144+
data "kubernetes_service" "formal_connector" {
145+
metadata {
146+
name = "formal-connector"
147+
namespace = var.namespace
148+
}
149+
150+
depends_on = [helm_release.formal_connector]
151+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
terraform {
2+
required_providers {
3+
formal = {
4+
source = "formalco/formal"
5+
version = "~> 4.12.8"
6+
}
7+
}
8+
}
9+
10+
provider "formal" {
11+
api_key = var.formal_api_key
12+
}
13+
14+
resource "formal_connector" "main" {
15+
name = var.name
16+
}
17+
18+
# Cloud SQL Resource
19+
# The connector connects to Cloud SQL via the Cloud SQL Proxy sidecar on localhost:5433
20+
# (port 5433 avoids conflict with the connector's listener on port 5432)
21+
resource "formal_resource" "cloudsql" {
22+
technology = "postgres"
23+
name = coalesce(var.resource_name, "${var.name}-postgres")
24+
hostname = "localhost"
25+
port = 5433
26+
}
27+
28+
# Disable TLS for the resource since Cloud SQL Proxy handles encryption
29+
resource "formal_resource_tls_configuration" "cloudsql" {
30+
resource_id = formal_resource.cloudsql.id
31+
tls_config = "disable"
32+
}
33+
34+
# Native user for Cloud SQL IAM authentication
35+
# The IAM database user is the service account email with .iam instead of .iam.gserviceaccount.com
36+
# The connector generates the IAM token using iam_gcp auth type
37+
resource "formal_native_user" "cloudsql_iam" {
38+
resource_id = formal_resource.cloudsql.id
39+
native_user_id = replace(var.gcp_service_account_email, ".gserviceaccount.com", "")
40+
native_user_secret = "iam_gcp"
41+
use_as_default = true
42+
}
43+
44+
# Postgres Listener
45+
resource "formal_connector_listener" "postgres_listener" {
46+
name = "${var.name}-postgres-listener"
47+
port = var.postgres_port
48+
connector_id = formal_connector.main.id
49+
}
50+
51+
resource "formal_connector_listener_rule" "postgres_rule" {
52+
connector_listener_id = formal_connector_listener.postgres_listener.id
53+
type = "technology"
54+
rule = "postgres"
55+
}
56+
57+
# Listener Link
58+
resource "formal_connector_listener_link" "postgres_link" {
59+
connector_id = formal_connector.main.id
60+
connector_listener_id = formal_connector_listener.postgres_listener.id
61+
}

0 commit comments

Comments
 (0)