From 763b08d124b7ecdc1d420634c1b0db4e69762172 Mon Sep 17 00:00:00 2001 From: Matt Moore Date: Wed, 20 Dec 2023 10:48:52 -0500 Subject: [PATCH] Restructure the dashboards to leverage `mosaicLayout` (#18) This layout is more cumbersome because of the requirement to explicitly lay things out, however, this change introduces a number of helper modules to make this a bit more tolerable. At a high-level, "sections" (our own intermediate concept) produce groupings of tiles laid out relative to `(0, 0)` with a width of `module.width.size` (see below). The modules in `./dashboard/sections` group our "widgets" into arrangements of "tiles" (e.g. logs, http, resources) with several special helper modules: 1. `width`: This module exists to define a global constant for the dashboard width, 1. `collapsible`: This wraps a list of tiles in a "collapsible" region with a title (it automagically figures out the bounding box, to avoid human error here), 1. `layout`: This takes a list of "sections" (defined above) and rebases them so that the `yPos` of each section starts after the preceding section. This also incorporates a change to give our networking module's DNS constructs unique names, so that we can provision multiple distinct private networks (I hit this creating a small example to test with, and it conflicted with my dev environment). Signed-off-by: Matt Moore --- dashboard/job/README.md | 10 +-- dashboard/job/dashboard.tf | 70 ++++----------- dashboard/sections/collapsible/main.tf | 24 +++++ dashboard/sections/http/main.tf | 60 +++++++++++++ dashboard/sections/layout/main.tf | 28 ++++++ dashboard/sections/logs/main.tf | 33 +++++++ dashboard/sections/resources/main.tf | 119 +++++++++++++++++++++++++ dashboard/sections/width/main.tf | 3 + dashboard/service/README.md | 14 ++- dashboard/service/dashboard.tf | 101 +++++---------------- networking/README.md | 2 + networking/dns.tf | 10 ++- 12 files changed, 328 insertions(+), 146 deletions(-) create mode 100644 dashboard/sections/collapsible/main.tf create mode 100644 dashboard/sections/http/main.tf create mode 100644 dashboard/sections/layout/main.tf create mode 100644 dashboard/sections/logs/main.tf create mode 100644 dashboard/sections/resources/main.tf create mode 100644 dashboard/sections/width/main.tf diff --git a/dashboard/job/README.md b/dashboard/job/README.md index 864f5195..9b9543ce 100644 --- a/dashboard/job/README.md +++ b/dashboard/job/README.md @@ -42,12 +42,10 @@ No requirements. | Name | Source | Version | |------|--------|---------| -| [cpu\_utilization](#module\_cpu\_utilization) | ../widgets/xy | n/a | -| [logs](#module\_logs) | ../widgets/logs | n/a | -| [memory\_utilization](#module\_memory\_utilization) | ../widgets/xy | n/a | -| [received\_bytes](#module\_received\_bytes) | ../widgets/xy | n/a | -| [sent\_bytes](#module\_sent\_bytes) | ../widgets/xy | n/a | -| [startup\_latency](#module\_startup\_latency) | ../widgets/xy | n/a | +| [layout](#module\_layout) | ../sections/layout | n/a | +| [logs](#module\_logs) | ../sections/logs | n/a | +| [resources](#module\_resources) | ../sections/resources | n/a | +| [width](#module\_width) | ../sections/width | n/a | ## Resources diff --git a/dashboard/job/dashboard.tf b/dashboard/job/dashboard.tf index c31216b9..e6f952d9 100644 --- a/dashboard/job/dashboard.tf +++ b/dashboard/job/dashboard.tf @@ -1,49 +1,23 @@ -locals { common_filter = ["resource.type=\"cloud_run_job\""] } - module "logs" { - source = "../widgets/logs" - title = "Service Logs" - filter = local.common_filter -} - -module "cpu_utilization" { - source = "../widgets/xy" - title = "CPU utilization" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/cpu/utilizations\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" + source = "../sections/logs" + title = "Job Logs" + filter = ["resource.type=\"cloud_run_job\""] } -module "memory_utilization" { - source = "../widgets/xy" - title = "Memory utilization" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/memory/utilizations\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" +module "resources" { + source = "../sections/resources" + title = "Resources" + filter = ["resource.type=\"cloud_run_job\""] } -module "startup_latency" { - source = "../widgets/xy" - title = "Startup latency" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/startup_latencies\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" -} +module "width" { source = "../sections/width" } -module "sent_bytes" { - source = "../widgets/xy" - title = "Sent bytes" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/network/sent_bytes_count\""]) - primary_align = "ALIGN_MEAN" - primary_reduce = "REDUCE_NONE" -} - -module "received_bytes" { - source = "../widgets/xy" - title = "Received bytes" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/network/received_bytes_count\""]) - primary_align = "ALIGN_MEAN" - primary_reduce = "REDUCE_NONE" +module "layout" { + source = "../sections/layout" + sections = [ + module.logs.section, + module.resources.section, + ] } resource "google_monitoring_dashboard" "dashboard" { @@ -54,17 +28,11 @@ resource "google_monitoring_dashboard" "dashboard" { stringValue = var.job_name labelKey = "job_name" }] - // https://cloud.google.com/monitoring/api/ref_v3/rest/v1/projects.dashboards#GridLayout - gridLayout = { - columns = 3 - widgets = [ - module.logs.widget, - module.cpu_utilization.widget, - module.memory_utilization.widget, - module.startup_latency.widget, - module.sent_bytes.widget, - module.received_bytes.widget, - ] + + // https://cloud.google.com/monitoring/api/ref_v3/rest/v1/projects.dashboards#mosaiclayout + mosaicLayout = { + columns = module.width.size + tiles = module.layout.tiles, } }) } diff --git a/dashboard/sections/collapsible/main.tf b/dashboard/sections/collapsible/main.tf new file mode 100644 index 00000000..7151294f --- /dev/null +++ b/dashboard/sections/collapsible/main.tf @@ -0,0 +1,24 @@ +variable "title" { type = string } +variable "tiles" {} +variable "collapsed" { default = false } + +locals { + start_row = min([for s in var.tiles : s.yPos]...) +} + +module "width" { source = "../width" } + +output "section" { + value = concat([{ + yPos = local.start_row + xPos = 0, + height = max([for s in var.tiles : s.yPos + s.height - local.start_row]...), + width = module.width.size, + widget = { + title = var.title + collapsibleGroup = { + collapsed = var.collapsed + } + }, + }], var.tiles) +} diff --git a/dashboard/sections/http/main.tf b/dashboard/sections/http/main.tf new file mode 100644 index 00000000..7ee26758 --- /dev/null +++ b/dashboard/sections/http/main.tf @@ -0,0 +1,60 @@ +variable "title" { type = string } +variable "filter" { type = list(string) } +variable "collapsed" { default = false } + +module "width" { source = "../width" } + +module "request_count" { + source = "../../widgets/xy" + title = "Request count" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/request_count\""]) + group_by_fields = ["metric.label.\"response_code_class\""] + primary_align = "ALIGN_RATE" + primary_reduce = "REDUCE_NONE" + secondary_align = "ALIGN_NONE" + secondary_reduce = "REDUCE_SUM" +} + +module "incoming_latency" { + source = "../../widgets/latency" + title = "Incoming request latency" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/request_latencies\""]) +} + +// TODO(mattmoor): output HTTP charts. + +locals { + columns = 2 + unit = module.width.size / local.columns + + // https://www.terraform.io/language/functions/range + // N columns, unit width each ([0, unit, 2 * unit, ...]) + col = range(0, local.columns * local.unit, local.unit) + + tiles = [{ + yPos = 0 + xPos = local.col[0], + height = local.unit, + width = local.unit, + widget = module.request_count.widget, + }, + { + yPos = 0 + xPos = local.col[1], + height = local.unit, + width = local.unit, + widget = module.incoming_latency.widget, + }] +} + +module "collapsible" { + source = "../collapsible" + + title = var.title + tiles = local.tiles + collapsed = var.collapsed +} + +output "section" { + value = module.collapsible.section +} diff --git a/dashboard/sections/layout/main.tf b/dashboard/sections/layout/main.tf new file mode 100644 index 00000000..bc0f045e --- /dev/null +++ b/dashboard/sections/layout/main.tf @@ -0,0 +1,28 @@ +variable "sections" {} + +module "width" { source = "../width" } + +locals { + // The maximum height of a tile in each section. + max_heights = [for s in var.sections : max([for t in s : t.yPos + t.height]...)] + + // The sum of the maximum tile heights in all of the sections prior to this one. + // Note: sum doesn't work on an empty list, so we concatenate with 0 for the base case. + sum_heights = [for s in var.sections : sum(concat([0], slice(local.max_heights, 0, index(var.sections, s))))] + + // Rebase the yPos of each tile in each section to be relative to the top of + // the section, which starts after the topmost tile of the preceding section. + rebased = [for s in var.sections : [ + for t in s : { + yPos = t.yPos + local.sum_heights[index(var.sections, s)] + xPos = t.xPos + height = t.height + width = t.width + widget = t.widget + }] + ] +} + +output "tiles" { + value = concat(local.rebased...) +} diff --git a/dashboard/sections/logs/main.tf b/dashboard/sections/logs/main.tf new file mode 100644 index 00000000..8ec9765b --- /dev/null +++ b/dashboard/sections/logs/main.tf @@ -0,0 +1,33 @@ +variable "title" { type = string } +variable "filter" { type = list(string) } +variable "collapsed" { default = true } + +module "width" { source = "../width" } + +module "logs" { + source = "../../widgets/logs" + title = var.title + filter = var.filter +} + +locals { + tiles = [{ + yPos = 0 + xPos = 0, + height = module.width.size, + width = module.width.size, + widget = module.logs.widget, + }] +} + +module "collapsible" { + source = "../collapsible" + + title = var.title + tiles = local.tiles + collapsed = var.collapsed +} + +output "section" { + value = module.collapsible.section +} diff --git a/dashboard/sections/resources/main.tf b/dashboard/sections/resources/main.tf new file mode 100644 index 00000000..097b144b --- /dev/null +++ b/dashboard/sections/resources/main.tf @@ -0,0 +1,119 @@ +variable "title" { type = string } +variable "filter" { type = list(string) } +variable "collapsed" { default = false } + +module "width" { source = "../width" } + +module "instance_count" { + source = "../../widgets/xy" + title = "Instance count + revisions" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/instance_count\""]) + group_by_fields = ["resource.label.\"revision_name\""] + primary_align = "ALIGN_MEAN" + primary_reduce = "REDUCE_SUM" + plot_type = "STACKED_AREA" +} + +module "cpu_utilization" { + source = "../../widgets/xy" + title = "CPU utilization" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/cpu/utilizations\""]) + primary_align = "ALIGN_DELTA" + primary_reduce = "REDUCE_MEAN" +} + +module "memory_utilization" { + source = "../../widgets/xy" + title = "Memory utilization" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/memory/utilizations\""]) + primary_align = "ALIGN_DELTA" + primary_reduce = "REDUCE_MEAN" +} + +module "startup_latency" { + source = "../../widgets/xy" + title = "Startup latency" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/startup_latencies\""]) + primary_align = "ALIGN_DELTA" + primary_reduce = "REDUCE_MEAN" +} + +module "sent_bytes" { + source = "../../widgets/xy" + title = "Sent bytes" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/network/sent_bytes_count\""]) + primary_align = "ALIGN_MEAN" + primary_reduce = "REDUCE_NONE" +} + +module "received_bytes" { + source = "../../widgets/xy" + title = "Received bytes" + filter = concat(var.filter, ["metric.type=\"run.googleapis.com/container/network/received_bytes_count\""]) + primary_align = "ALIGN_MEAN" + primary_reduce = "REDUCE_NONE" +} + +locals { + columns = 3 + unit = module.width.size / local.columns + + // https://www.terraform.io/language/functions/range + // N columns, unit width each ([0, unit, 2 * unit, ...]) + col = range(0, local.columns * local.unit, local.unit) + + tiles = [{ + yPos = 0, + xPos = local.col[0], + height = local.unit, + width = local.unit, + widget = module.cpu_utilization.widget, + }, + { + yPos = 0, + xPos = local.col[1], + height = local.unit, + width = local.unit, + widget = module.memory_utilization.widget, + }, + { + yPos = 0, + xPos = local.col[2], + height = local.unit, + width = local.unit, + widget = module.instance_count.widget, + }, + { + yPos = local.unit, + xPos = local.col[0], + height = local.unit, + width = local.unit, + widget = module.startup_latency.widget, + }, + { + yPos = local.unit, + xPos = local.col[1], + height = local.unit, + width = local.unit, + widget = module.sent_bytes.widget, + }, + { + yPos = local.unit, + xPos = local.col[2], + height = local.unit, + width = local.unit, + widget = module.received_bytes.widget, + }] +} + +module "collapsible" { + source = "../collapsible" + + title = var.title + tiles = local.tiles + collapsed = var.collapsed +} + +output "section" { + value = module.collapsible.section +} diff --git a/dashboard/sections/width/main.tf b/dashboard/sections/width/main.tf new file mode 100644 index 00000000..2f90fd14 --- /dev/null +++ b/dashboard/sections/width/main.tf @@ -0,0 +1,3 @@ +output "size" { + value = 12 +} diff --git a/dashboard/service/README.md b/dashboard/service/README.md index 66a206fc..c30ce0b4 100644 --- a/dashboard/service/README.md +++ b/dashboard/service/README.md @@ -52,15 +52,11 @@ No requirements. | Name | Source | Version | |------|--------|---------| -| [cpu\_utilization](#module\_cpu\_utilization) | ../widgets/xy | n/a | -| [incoming\_latency](#module\_incoming\_latency) | ../widgets/latency | n/a | -| [instance\_count](#module\_instance\_count) | ../widgets/xy | n/a | -| [logs](#module\_logs) | ../widgets/logs | n/a | -| [memory\_utilization](#module\_memory\_utilization) | ../widgets/xy | n/a | -| [received\_bytes](#module\_received\_bytes) | ../widgets/xy | n/a | -| [request\_count](#module\_request\_count) | ../widgets/xy | n/a | -| [sent\_bytes](#module\_sent\_bytes) | ../widgets/xy | n/a | -| [startup\_latency](#module\_startup\_latency) | ../widgets/xy | n/a | +| [http](#module\_http) | ../sections/http | n/a | +| [layout](#module\_layout) | ../sections/layout | n/a | +| [logs](#module\_logs) | ../sections/logs | n/a | +| [resources](#module\_resources) | ../sections/resources | n/a | +| [width](#module\_width) | ../sections/width | n/a | ## Resources diff --git a/dashboard/service/dashboard.tf b/dashboard/service/dashboard.tf index 4f87b722..9386a7fe 100644 --- a/dashboard/service/dashboard.tf +++ b/dashboard/service/dashboard.tf @@ -1,76 +1,30 @@ -locals { common_filter = ["resource.type=\"cloud_run_revision\""] } - module "logs" { - source = "../widgets/logs" + source = "../sections/logs" title = "Service Logs" - filter = local.common_filter -} - -module "request_count" { - source = "../widgets/xy" - title = "Request count" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/request_count\""]) - group_by_fields = ["metric.label.\"response_code_class\""] - primary_align = "ALIGN_RATE" - primary_reduce = "REDUCE_NONE" - secondary_align = "ALIGN_NONE" - secondary_reduce = "REDUCE_SUM" + filter = ["resource.type=\"cloud_run_revision\""] } -module "incoming_latency" { - source = "../widgets/latency" - title = "Incoming request latency" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/request_latencies\""]) +module "http" { + source = "../sections/http" + title = "HTTP" + filter = ["resource.type=\"cloud_run_revision\""] } -module "instance_count" { - source = "../widgets/xy" - title = "Instance count + revisions" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/instance_count\""]) - group_by_fields = ["resource.label.\"revision_name\""] - primary_align = "ALIGN_MEAN" - primary_reduce = "REDUCE_SUM" - plot_type = "STACKED_AREA" +module "resources" { + source = "../sections/resources" + title = "Resources" + filter = ["resource.type=\"cloud_run_revision\""] } -module "cpu_utilization" { - source = "../widgets/xy" - title = "CPU utilization" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/cpu/utilizations\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" -} - -module "memory_utilization" { - source = "../widgets/xy" - title = "Memory utilization" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/memory/utilizations\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" -} +module "width" { source = "../sections/width" } -module "startup_latency" { - source = "../widgets/xy" - title = "Startup latency" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/startup_latencies\""]) - primary_align = "ALIGN_DELTA" - primary_reduce = "REDUCE_MEAN" -} - -module "sent_bytes" { - source = "../widgets/xy" - title = "Sent bytes" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/network/sent_bytes_count\""]) - primary_align = "ALIGN_MEAN" - primary_reduce = "REDUCE_NONE" -} - -module "received_bytes" { - source = "../widgets/xy" - title = "Received bytes" - filter = concat(local.common_filter, ["metric.type=\"run.googleapis.com/container/network/received_bytes_count\""]) - primary_align = "ALIGN_MEAN" - primary_reduce = "REDUCE_NONE" +module "layout" { + source = "../sections/layout" + sections = [ + module.logs.section, + module.http.section, + module.resources.section, + ] } resource "google_monitoring_dashboard" "dashboard" { @@ -81,20 +35,11 @@ resource "google_monitoring_dashboard" "dashboard" { stringValue = var.service_name labelKey = "service_name" }] - // https://cloud.google.com/monitoring/api/ref_v3/rest/v1/projects.dashboards#GridLayout - gridLayout = { - columns = 3 - widgets = [ - module.logs.widget, - module.request_count.widget, - module.incoming_latency.widget, - module.instance_count.widget, - module.cpu_utilization.widget, - module.memory_utilization.widget, - module.startup_latency.widget, - module.sent_bytes.widget, - module.received_bytes.widget, - ] + + // https://cloud.google.com/monitoring/api/ref_v3/rest/v1/projects.dashboards#mosaiclayout + mosaicLayout = { + columns = module.width.size + tiles = module.layout.tiles, } }) } diff --git a/networking/README.md b/networking/README.md index ec6ed2b2..2e43fc2c 100644 --- a/networking/README.md +++ b/networking/README.md @@ -36,6 +36,7 @@ No requirements. | Name | Version | |------|---------| | [google](#provider\_google) | n/a | +| [random](#provider\_random) | n/a | ## Modules @@ -52,6 +53,7 @@ No modules. | [google_dns_managed_zone.private-google-apis](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | | [google_dns_record_set.cloud-run-cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | | [google_dns_record_set.private-googleapis-a-record](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | ## Inputs diff --git a/networking/dns.tf b/networking/dns.tf index 20806a4d..28707f7d 100644 --- a/networking/dns.tf +++ b/networking/dns.tf @@ -1,9 +1,15 @@ +resource "random_string" "suffix" { + length = 4 + upper = false + special = false +} + // Create a special DNS zone attached to the network in which // we will operate our services that reroutes *.run.app to records // that we control. resource "google_dns_managed_zone" "cloud-run-internal" { project = var.project_id - name = "cloud-run-internal" + name = "cloud-run-internal-${random_string.suffix.result}" dns_name = "run.app." description = "This reroutes run.app requests to private.googleapis.com" @@ -32,7 +38,7 @@ resource "google_dns_record_set" "cloud-run-cname" { // to records that we control. resource "google_dns_managed_zone" "private-google-apis" { project = var.project_id - name = "private-google-apis" + name = "private-google-apis-${random_string.suffix.result}" dns_name = "private.googleapis.com." description = "This maps DNS for private.googleapis.com"