Skip to content

Commit

Permalink
Add a module to encapsulate some of our Go service best practices.
Browse files Browse the repository at this point in the history
This module encapsulates much of the boilerplate to stand up a regionalized Go service from source using
`ko_build`.

Signed-off-by: Matt Moore <[email protected]>
  • Loading branch information
mattmoor committed Dec 24, 2023
1 parent 100bc25 commit 05928b3
Show file tree
Hide file tree
Showing 14 changed files with 412 additions and 225 deletions.
1 change: 1 addition & 0 deletions .github/workflows/documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
- cloudevent-broker
- cloudevent-trigger
- cloudevent-recorder
- regional-go-service
- otel-collector
- networking
- dashboard/service
Expand Down
49 changes: 17 additions & 32 deletions cloudevent-broker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,25 @@ module "foo-emits-events" {
// Run a cloud run service as the "foo" service account, and pass in the address
// of the regional ingress endpoint.
resource "google_cloud_run_v2_service" "foo-service" {
for_each = module.networking.regional-networks
project = var.project_id
name = "foo"
location = each.key
launch_stage = "BETA"
module "foo-service" {
source = "chainguard-dev/glue/cloudrun//regional-go-service"
template {
vpc_access {
network_interfaces {
network = each.value.network
subnetwork = each.value.subnet
}
// Egress through VPC so we can talk to the private ingress endpoint.
egress = "PRIVATE_RANGES_ONLY"
}
service_account = google_service_account.foo.email
containers {
image = "..."
project_id = var.project_id
name = "foo"
regions = module.networking.regional-networks
// Pass the resolved regional URI to the service in this region.
env {
name = "EVENT_INGRESS_URL"
value = module.foo-emits-events[each.key].uri
service_account = google_service_account.foo.email
containers = {
"foo" = {
source = {
working_dir = path.module
importpath = "./cmd/foo"
}
ports = [{ container_port = 8080 }]
regional-env = [{
name = "PUBSUB_TOPIC"
value = { for k, v in module.foo-emits-events : k => v.uri }
}]
}
}
}
Expand All @@ -82,9 +72,7 @@ No requirements.

| Name | Version |
|------|---------|
| <a name="provider_cosign"></a> [cosign](#provider\_cosign) | n/a |
| <a name="provider_google"></a> [google](#provider\_google) | n/a |
| <a name="provider_ko"></a> [ko](#provider\_ko) | n/a |

## Modules

Expand All @@ -93,22 +81,19 @@ No requirements.
| <a name="module_http"></a> [http](#module\_http) | ../dashboard/sections/http | n/a |
| <a name="module_layout"></a> [layout](#module\_layout) | ../dashboard/sections/layout | n/a |
| <a name="module_logs"></a> [logs](#module\_logs) | ../dashboard/sections/logs | n/a |
| <a name="module_otel-collector"></a> [otel-collector](#module\_otel-collector) | ../otel-collector | n/a |
| <a name="module_resources"></a> [resources](#module\_resources) | ../dashboard/sections/resources | n/a |
| <a name="module_this"></a> [this](#module\_this) | ../regional-go-service | n/a |
| <a name="module_topic"></a> [topic](#module\_topic) | ../dashboard/sections/topic | n/a |
| <a name="module_width"></a> [width](#module\_width) | ../dashboard/sections/width | n/a |

## Resources

| Name | Type |
|------|------|
| [cosign_sign.this](https://registry.terraform.io/providers/chainguard-dev/cosign/latest/docs/resources/sign) | resource |
| [google_cloud_run_v2_service.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service) | resource |
| [google_monitoring_dashboard.dashboard](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/monitoring_dashboard) | resource |
| [google_pubsub_topic.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic) | resource |
| [google_pubsub_topic_iam_binding.ingress-publishes-events](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic_iam_binding) | resource |
| [google_service_account.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |
| [ko_build.this](https://registry.terraform.io/providers/ko-build/ko/latest/docs/resources/build) | resource |

## Inputs

Expand Down
72 changes: 14 additions & 58 deletions cloudevent-broker/ingress.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,69 +21,25 @@ resource "google_pubsub_topic_iam_binding" "ingress-publishes-events" {
members = ["serviceAccount:${google_service_account.this.email}"]
}

// Build the ingress image using our minimal hardened base image.
resource "ko_build" "this" {
base_image = "cgr.dev/chainguard/static:latest-glibc"
importpath = "./cmd/ingress"
working_dir = path.module
}

// Sign the image, assuming a keyless signing identity is available.
resource "cosign_sign" "this" {
image = ko_build.this.image_ref

# Only keep the latest signature.
conflict = "REPLACE"
}

module "otel-collector" {
source = "../otel-collector"
module "this" {
source = "../regional-go-service"
project_id = var.project_id
name = var.name
regions = var.regions

project_id = var.project_id
service_account = google_service_account.this.email
}

resource "google_cloud_run_v2_service" "this" {
for_each = var.regions

// Explicitly wait for the iam binding before provisioning the service,
// since the service functionally depends on being able to publish events
// to the topic. In practice, GCP IAM is "eventually consistent" and there
// will still invariably be some latency after even the service is created
// where publishing may fail.
depends_on = [google_pubsub_topic_iam_binding.ingress-publishes-events]

project = var.project_id
name = var.name
location = each.key

// The ingress service is an internal service, and so it should only
// be exposed to the internal network.
ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"

launch_stage = "BETA" // Needed for vpc_access below

template {
vpc_access {
network_interfaces {
network = each.value.network
subnetwork = each.value.subnet
containers = {
"ingress" = {
source = {
working_dir = path.module
importpath = "./cmd/ingress"
}
egress = "ALL_TRAFFIC" // This should not egress
}

service_account = google_service_account.this.email
containers {
image = cosign_sign.this.signed_ref

ports { container_port = 8080 }

env {
ports = [{ container_port = 8080 }]
regional-env = [{
name = "PUBSUB_TOPIC"
value = google_pubsub_topic.this[each.key].name
}
value = { for k, v in google_pubsub_topic.this : k => v.name }
}]
}
containers { image = module.otel-collector.image }
}
}

Expand Down
2 changes: 1 addition & 1 deletion cloudevent-broker/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
output "ingress" {
depends_on = [google_cloud_run_v2_service.this]
depends_on = [module.this]
description = "An object holding the name of the ingress service, which can be used to authorize callers to publish cloud events."
value = {
name = var.name
Expand Down
10 changes: 1 addition & 9 deletions cloudevent-recorder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,21 @@ No requirements.

| Name | Version |
|------|---------|
| <a name="provider_cosign"></a> [cosign](#provider\_cosign) | n/a |
| <a name="provider_google"></a> [google](#provider\_google) | n/a |
| <a name="provider_google-beta"></a> [google-beta](#provider\_google-beta) | n/a |
| <a name="provider_ko"></a> [ko](#provider\_ko) | n/a |
| <a name="provider_random"></a> [random](#provider\_random) | n/a |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_otel-collector"></a> [otel-collector](#module\_otel-collector) | ../otel-collector | n/a |
| <a name="module_recorder-dashboard"></a> [recorder-dashboard](#module\_recorder-dashboard) | ../dashboard/cloudevent-receiver | n/a |
| <a name="module_this"></a> [this](#module\_this) | ../regional-go-service | n/a |
| <a name="module_triggers"></a> [triggers](#module\_triggers) | ../cloudevent-trigger | n/a |

## Resources

| Name | Type |
|------|------|
| [cosign_sign.logrotate-image](https://registry.terraform.io/providers/chainguard-dev/cosign/latest/docs/resources/sign) | resource |
| [cosign_sign.recorder-image](https://registry.terraform.io/providers/chainguard-dev/cosign/latest/docs/resources/sign) | resource |
| [google-beta_google_cloud_run_v2_service.recorder-service](https://registry.terraform.io/providers/hashicorp/google-beta/latest/docs/resources/google_cloud_run_v2_service) | resource |
| [google_bigquery_data_transfer_config.import-job](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/bigquery_data_transfer_config) | resource |
| [google_bigquery_dataset.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/bigquery_dataset) | resource |
| [google_bigquery_table.types](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/bigquery_table) | resource |
Expand All @@ -84,8 +78,6 @@ No requirements.
| [google_storage_bucket.recorder](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource |
| [google_storage_bucket_iam_member.import-reads-from-gcs-buckets](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource |
| [google_storage_bucket_iam_member.recorder-writes-to-gcs-buckets](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource |
| [ko_build.logrotate-image](https://registry.terraform.io/providers/ko-build/ko/latest/docs/resources/build) | resource |
| [ko_build.recorder-image](https://registry.terraform.io/providers/ko-build/ko/latest/docs/resources/build) | resource |
| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [random_id.trigger-suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source |
Expand Down
118 changes: 34 additions & 84 deletions cloudevent-recorder/recorder.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,100 +16,52 @@ resource "google_storage_bucket_iam_member" "recorder-writes-to-gcs-buckets" {
member = "serviceAccount:${google_service_account.recorder.email}"
}

resource "ko_build" "recorder-image" {
base_image = "cgr.dev/chainguard/static:latest-glibc"
importpath = "./cmd/recorder"
working_dir = path.module
}

resource "cosign_sign" "recorder-image" {
image = ko_build.recorder-image.image_ref

# Only keep the latest signature.
conflict = "REPLACE"
}

resource "ko_build" "logrotate-image" {
base_image = "cgr.dev/chainguard/static:latest-glibc"
importpath = "./cmd/logrotate"
working_dir = path.module
}

resource "cosign_sign" "logrotate-image" {
image = ko_build.logrotate-image.image_ref

# Only keep the latest signature.
conflict = "REPLACE"
}

module "otel-collector" {
source = "../otel-collector"
module "this" {
source = "../regional-go-service"
project_id = var.project_id
name = var.name
regions = var.regions

project_id = var.project_id
service_account = google_service_account.recorder.email
}

resource "google_cloud_run_v2_service" "recorder-service" {
for_each = var.regions

provider = google-beta # For empty_dir
project = var.project_id
name = var.name
location = each.key
// This service should only be called by our Pub/Sub
// subscription, so flag it as internal only.
ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"

launch_stage = "BETA"

template {
vpc_access {
network_interfaces {
network = each.value.network
subnetwork = each.value.subnet
}
egress = "ALL_TRAFFIC" // This should not egress
}

service_account = google_service_account.recorder.email
containers {
image = cosign_sign.recorder-image.signed_ref

ports {
container_port = 8080
containers = {
"recorder" = {
source = {
working_dir = path.module
importpath = "./cmd/recorder"
}

env {
ports = [{ container_port = 8080 }]
env = [{
name = "LOG_PATH"
value = "/logs"
}
volume_mounts {
}]
volume_mounts = [{
name = "logs"
mount_path = "/logs"
}
}]
}
containers {
image = cosign_sign.logrotate-image.signed_ref

env {
name = "BUCKET"
value = google_storage_bucket.recorder[each.key].url
"logrotate" = {
source = {
working_dir = path.module
importpath = "./cmd/logrotate"
}
env {
env = [{
name = "LOG_PATH"
value = "/logs"
}
volume_mounts {
}]
regional-env = [{
name = "BUCKET"
value = { for k, v in google_storage_bucket.recorder : k => v.url }
}]
volume_mounts = [{
name = "logs"
mount_path = "/logs"
}
}
containers { image = module.otel-collector.image }
volumes {
name = "logs"
empty_dir {}
}]
}
}
volumes = [{
name = "logs"
empty_dir = {}
}]
}

resource "random_id" "trigger-suffix" {
Expand All @@ -128,20 +80,18 @@ module "triggers" {
broker = var.broker[each.value.region]
filter = { "type" : each.value.type }

depends_on = [google_cloud_run_v2_service.recorder-service]
depends_on = [module.this]
private-service = {
region = each.value.region
name = google_cloud_run_v2_service.recorder-service[each.value.region].name
name = var.name
}
}

module "recorder-dashboard" {
source = "../dashboard/cloudevent-receiver"
service_name = var.name

labels = {
for type, schema in var.types : replace(type, ".", "_") => ""
}
labels = { for type, schema in var.types : replace(type, ".", "_") => "" }

triggers = {
for type, schema in var.types : "type: ${type}" => "${var.name}-${random_id.trigger-suffix[type].hex}"
Expand Down
Loading

0 comments on commit 05928b3

Please sign in to comment.