Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a serverless-gclb module. #79

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
- cloudevent-trigger
- cloudevent-recorder
- regional-go-service
- serverless-gclb
- otel-collector
- networking
- dashboard/service
Expand Down
8 changes: 4 additions & 4 deletions modules/dashboard/service/dashboard.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ module "http" {
}

module "grpc" {
source = "../sections/grpc"
title = "GRPC"
filter = []
service_name = var.service_name
source = "../sections/grpc"
title = "GRPC"
filter = []
service_name = var.service_name
}

module "resources" {
Expand Down
2 changes: 1 addition & 1 deletion modules/regional-go-service/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ resource "google_cloud_run_v2_service" "this" {
for_each = env.value.value_source != null ? { "" : env.value.value_source } : {}
content {
secret_key_ref {
secret = value_source.value.secret_key_ref.secret
secret = value_source.value.secret_key_ref.secret
version = value_source.value.secret_key_ref.version
}
}
Expand Down
84 changes: 84 additions & 0 deletions modules/serverless-gclb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# `serverless-gclb`

This module provisions a Google Cloud Load Balancer (GCLB) that sits in front of
some number of regionalized Cloud Run services.

```hcl
// Create a network with several regional subnets
module "networking" {
source = "chainguard-dev/common/infra//modules/networking"

name = "my-networking"
project_id = var.project_id
regions = [...]
}

resource "google_dns_managed_zone" "top-level-zone" {
project = var.project_id
name = "example-com"
dns_name = "example.com."
}

module "serverless-gclb" {
source = "chainguard-dev/common/infra//modules/serverless-gclb"

name = "my-gclb"
project_id = var.project_id
dns_zone = google_dns_managed_zone.top-level-zone.name

// Regions are all of the places that we have backends deployed.
// Regions must be removed from serving before they are torn down.
regions = keys(module.networking.regional-networks)
serving_regions = keys(module.networking.regional-networks)

public-services = {
"foo.example.com" = {
name = "my-foo-service" // e.g. from regional-go-service
}
}
}
```

<!-- BEGIN_TF_DOCS -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | n/a |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [google_compute_backend_service.public-services](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_backend_service) | resource |
| [google_compute_global_address.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_global_address) | resource |
| [google_compute_global_forwarding_rule.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_global_forwarding_rule) | resource |
| [google_compute_managed_ssl_certificate.public-service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_managed_ssl_certificate) | resource |
| [google_compute_region_network_endpoint_group.regional-backends](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_network_endpoint_group) | resource |
| [google_compute_target_https_proxy.public-service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_target_https_proxy) | resource |
| [google_compute_url_map.public-service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map) | resource |
| [google_dns_record_set.public-service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_dns_zone"></a> [dns\_zone](#input\_dns\_zone) | The managed DNS zone in which to create record sets. | `string` | n/a | yes |
| <a name="input_name"></a> [name](#input\_name) | n/a | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | n/a | `string` | n/a | yes |
| <a name="input_public-services"></a> [public-services](#input\_public-services) | A map from hostnames (managed by dns\_zone), to the name of the regionalized cloud run service to which the hostname should be routed. A managed SSL certificate will be created for each hostname, and a DNS record set will be created for each hostname pointing to the load balancer's global IP address. | <pre>map(object({<br> name = string<br> }))</pre> | n/a | yes |
| <a name="input_regions"></a> [regions](#input\_regions) | The set of regions containing backends for the load balancer (regions must be added here before they can be added as serving regions). | `list` | <pre>[<br> "us-central1"<br>]</pre> | no |
| <a name="input_serving_regions"></a> [serving\_regions](#input\_serving\_regions) | The set of regions with backends suitable for serving traffic from the load balancer (regions must be removed from here before they can be removed from regions). | `list` | <pre>[<br> "us-central1"<br>]</pre> | no |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
121 changes: 121 additions & 0 deletions modules/serverless-gclb/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Create the IP address for our LB to serve on.
resource "google_compute_global_address" "this" {
project = var.project_id
name = var.name
}

// Create A records for each of our public service hostnames.
resource "google_dns_record_set" "public-service" {
for_each = var.public-services

project = var.project_id
name = "${each.key}."
managed_zone = var.dns_zone
type = "A"
ttl = 60

rrdatas = [google_compute_global_address.this.address]
}

// Provision a managed SSL certificate for each of our public services.
resource "google_compute_managed_ssl_certificate" "public-service" {
for_each = var.public-services

name = each.value.name

managed {
domains = [google_dns_record_set.public-service[each.key].name]
}
}

// Create the cross-product of public services and regions so we can for_each over it.
locals {
regional-backends = merge([
for svcinfo in values(var.public-services) : merge([
for region in var.regions : {
"${svcinfo.name}-${region}" : {
name = svcinfo.name
region = region
}
}
]...)
]...)
}

// Create a network endpoint group for each service in each region.
resource "google_compute_region_network_endpoint_group" "regional-backends" {
for_each = local.regional-backends

name = each.value.name
network_endpoint_type = "SERVERLESS"
region = each.value.region
cloud_run {
service = each.value.name
}
}

// Create a backend service for each public service with a backend in each region.
resource "google_compute_backend_service" "public-services" {
for_each = var.public-services

project = var.project_id
name = each.value.name

// Create a backend for each region hosting this cloud run service.
dynamic "backend" {
for_each = toset(var.serving_regions)
content {
group = google_compute_region_network_endpoint_group.regional-backends["${each.value.name}-${backend.key}"]["id"]
}
}
}

// Create a URL map that routes each hostname to the appropriate backend service.
resource "google_compute_url_map" "public-service" {
project = var.project_id
name = var.name

default_url_redirect {
host_redirect = "chainguard.dev"
strip_query = true
}

// For each of the public services create a host rule.
dynamic "host_rule" {
for_each = var.public-services
content {
hosts = [host_rule.key]
path_matcher = host_rule.value.name
}
}

// For each of the public services create an empty path matcher
// that routes to its backend service.
dynamic "path_matcher" {
for_each = var.public-services
content {
name = path_matcher.value.name
default_service = google_compute_backend_service.public-services[path_matcher.key].id
}
}
}

// Create an HTTPS proxy for our URL map.
resource "google_compute_target_https_proxy" "public-service" {
project = var.project_id
name = var.name
url_map = google_compute_url_map.public-service.id

ssl_certificates = [for domain, cert in google_compute_managed_ssl_certificate.public-service : cert.id]
}

// Attach the HTTPS proxy to the global IP address via a forwarding rule.
resource "google_compute_global_forwarding_rule" "this" {
project = var.project_id
name = var.name
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = 443
ip_address = google_compute_global_address.this.id
target = google_compute_target_https_proxy.public-service.id
}
29 changes: 29 additions & 0 deletions modules/serverless-gclb/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
variable "name" {
type = string
}

variable "project_id" {
type = string
}

variable "regions" {
description = "The set of regions containing backends for the load balancer (regions must be added here before they can be added as serving regions)."
default = ["us-central1"]
}

variable "serving_regions" {
description = "The set of regions with backends suitable for serving traffic from the load balancer (regions must be removed from here before they can be removed from regions)."
default = ["us-central1"]
}

variable "dns_zone" {
type = string
description = "The managed DNS zone in which to create record sets."
}

variable "public-services" {
description = "A map from hostnames (managed by dns_zone), to the name of the regionalized cloud run service to which the hostname should be routed. A managed SSL certificate will be created for each hostname, and a DNS record set will be created for each hostname pointing to the load balancer's global IP address."
type = map(object({
name = string
}))
}
Loading