Skip to content
Open
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
2 changes: 1 addition & 1 deletion modules/gcs/standard/1.0/bucket.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ resource "google_storage_bucket" "bucket" {
name = local.bucket_name
location = local.location
storage_class = var.instance.spec.storage_class
project = var.inputs.cloud_account.attributes.project
project = var.inputs.cloud_account.attributes.project_id

uniform_bucket_level_access = var.instance.spec.uniform_bucket_level_access
requester_pays = var.instance.spec.requester_pays
Expand Down
2 changes: 1 addition & 1 deletion modules/gcs/standard/1.0/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ variable "inputs" {
type = object({
cloud_account = object({
attributes = object({
project = string
project_id = string
region = string
credentials = optional(string)
})
Expand Down
405 changes: 405 additions & 0 deletions modules/log_collector/loki_gcs/1.0/facets.yaml

Large diffs are not rendered by default.

474 changes: 474 additions & 0 deletions modules/log_collector/loki_gcs/1.0/locals.tf

Large diffs are not rendered by default.

120 changes: 120 additions & 0 deletions modules/log_collector/loki_gcs/1.0/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# ── GCP Service Account ───────────────────────────────────────────────────────
resource "google_service_account" "loki_gcs" {
account_id = local.sa_name
project = local.project
display_name = "Loki GCS backend service account"

lifecycle {
ignore_changes = [account_id]
}
}

# ── Custom IAM Role — minimal GCS permissions ─────────────────────────────────
resource "google_project_iam_custom_role" "loki_gcs" {
role_id = replace("${var.instance_name}_${local.env_name}_lokiGcsRole", "-", "_")
project = local.project
title = "Loki GCS backend role (${var.instance_name})"
description = "Minimal permissions for Loki to read/write GCS bucket"
permissions = [
"storage.buckets.get",
"storage.objects.create",
"storage.objects.delete",
"storage.objects.get",
"storage.objects.list",
]
}

# ── Bind custom role to the service account at project level ──────────────────
resource "google_project_iam_member" "loki_gcs" {
project = local.project
role = google_project_iam_custom_role.loki_gcs.id
member = "serviceAccount:${google_service_account.loki_gcs.email}"
}

# ── Bind GCS bucket IAM so the SA can access the bucket ───────────────────────
resource "google_storage_bucket_iam_binding" "loki_gcs" {
bucket = local.bucket_name
role = google_project_iam_custom_role.loki_gcs.id
members = [
"serviceAccount:${google_service_account.loki_gcs.email}",
]
}

# ── Workload Identity — allow k8s SA to impersonate the GCP SA ────────────────
# Unified grafana/loki chart creates k8s SA named <release> (the release name itself)
resource "google_service_account_iam_member" "loki_gcs_workload_identity" {
service_account_id = google_service_account.loki_gcs.id
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${local.project}.svc.id.goog[${local.namespace}/${var.instance_name}]"
}

# ── Helm: grafana/loki (unified chart) ────────────────────────────────────────
resource "helm_release" "loki" {
repository = "https://grafana.github.io/helm-charts"
chart = "loki"
name = var.instance_name
version = local.loki_chart_version
namespace = local.namespace
create_namespace = true
cleanup_on_fail = true
timeout = 600
wait = true

values = [
yamlencode(local.default_loki_values),
yamlencode(local.loki_user_values),
]
}

# ── Helm: promtail ────────────────────────────────────────────────────────────
resource "helm_release" "promtail" {
count = local.promtail_enabled ? 1 : 0

depends_on = [helm_release.loki]
repository = "https://grafana.github.io/helm-charts"
chart = "promtail"
name = "${var.instance_name}-promtail"
version = local.promtail_chart_version
namespace = local.namespace
create_namespace = true
cleanup_on_fail = true
timeout = 600
wait = false

values = [
yamlencode(local.default_promtail_values),
yamlencode(local.promtail_user_values),
]
}

# ── Grafana datasource ConfigMap (picked up by Grafana sidecar) ───────────────
resource "kubernetes_config_map_v1" "grafana_loki_datasource" {
depends_on = [helm_release.loki]

metadata {
name = "${var.instance_name}-loki-datasource"
namespace = local.namespace
labels = {
grafana_datasource = "1"
datasource_name = var.instance_name
}
}

data = {
"datasource-loki-${var.instance_name}.yaml" = yamlencode({
apiVersion = 1
datasources = [
{
name = "Loki ${var.instance_name}"
type = "loki"
url = "http://${local.loki_endpoint}"
access = "proxy"
isDefault = false
jsonData = {
timeout = 300
}
}
]
})
}
}
14 changes: 14 additions & 0 deletions modules/log_collector/loki_gcs/1.0/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
locals {
output_attributes = {
loki_url = "http://${local.loki_endpoint}"
namespace = local.namespace
service_account = google_service_account.loki_gcs.email
bucket_name = local.bucket_name
}

output_interfaces = {
default = {
url = "http://${local.loki_endpoint}"
}
}
}
23 changes: 23 additions & 0 deletions modules/log_collector/loki_gcs/1.0/outputs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
properties:
type: object
properties:
attributes:
type: object
properties:
loki_url:
type: string
namespace:
type: string
service_account:
type: string
bucket_name:
type: string
interfaces:
type: object
properties:
default:
type: object
properties:
url:
type: string
providers: []
40 changes: 40 additions & 0 deletions modules/log_collector/loki_gcs/1.0/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
variable "instance" {
description = "The full Facets resource instance object (kind, flavor, version, spec)"
type = any
}

variable "instance_name" {
description = "Unique architectural name from the blueprint"
type = string
}

variable "environment" {
description = "Facets environment object"
type = any
}

variable "inputs" {
description = "Typed input dependencies from other Facets resources"
type = object({
kubernetes_details = object({
attributes = any
})
cloud_account = object({
attributes = object({
project_id = string
})
})
storage = object({
attributes = object({
bucket_name = string
})
})
kubernetes_node_pool_details = optional(object({
attributes = optional(object({
taints = optional(list(any), [])
node_selector = optional(map(string), {})
}), {})
interfaces = optional(object({}), {})
}))
})
}
24 changes: 24 additions & 0 deletions outputs/loki/outputs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: '@facets/loki'
properties:
type: object
properties:
attributes:
type: object
properties:
loki_url:
type: string
namespace:
type: string
service_account:
type: string
bucket_name:
type: string
interfaces:
type: object
properties:
default:
type: object
properties:
url:
type: string
providers: []
2 changes: 2 additions & 0 deletions project-type/gcp/project-type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ modules:
flavor: mongo
- intent: prometheus
flavor: k8s_standard
- intent: log_collector
flavor: loki_gcs
- intent: strimzi-operator
flavor: helm
- intent: vpa
Expand Down