Skip to content
Closed
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
18 changes: 0 additions & 18 deletions terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,6 @@ terraform apply -var-file=envs/gke/dev-<username>.tfvars \
After applying with `enable_pubsub=true`, you'll get these outputs:

```bash
# Get service account emails for Helm values
terraform output sentinel_service_account_email
terraform output adapter_service_account_email

# Get topic/subscription names
terraform output topic_name
terraform output subscription_name
Expand All @@ -168,20 +164,6 @@ Get the complete Helm values snippet (includes broker config and Workload Identi
terraform output helm_values_snippet
```

Or manually add the Workload Identity annotations:

```yaml
sentinel:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: <sentinel_service_account_email>

hyperfleet-adapter:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: <adapter_service_account_email>
```

## Directory Structure

```
Expand Down
10 changes: 4 additions & 6 deletions terraform/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,13 @@ module "pubsub" {
source = "./modules/pubsub"
count = var.enable_pubsub ? 1 : 0

project_id = var.gcp_project_id
namespace = var.kubernetes_namespace
resource_type = "clusters"
project_id = var.gcp_project_id
namespace = var.developer_name
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check variable definition and any validation rules for developer_name
rg -nP 'variable\s+"developer_name"' --type=tf -A15
# Check other usages of developer_name to understand the pattern
rg -nP 'var\.developer_name' --type=tf -C3

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 1694


Add validation to enforce Kubernetes namespace naming conventions on var.developer_name.

The developer_name variable currently lacks input validation. Kubernetes namespace names must be valid RFC 1123 DNS labels, which can only contain alphanumeric characters and hyphens (max 253 characters), with names unable to start with a hyphen and alpha characters only lowercase. Since developer usernames typically contain uppercase letters, underscores, or dots, the variable should enforce a regex pattern to validate compatibility before it's used as a namespace name. This prevents deployment failures from invalid Kubernetes resource names.

🤖 Prompt for AI Agents
In terraform/main.tf around line 57, var.developer_name is used as a Kubernetes
namespace but lacks validation; add a validation rule to the variable
declaration (typically in variables.tf or main.tf) that enforces the RFC1123 DNS
label constraints: only lowercase alphanumeric characters and hyphens, must
start and end with an alphanumeric character, and length <= 253 characters;
implement this by adding a regex-based validation (and an explanatory error
message) to the developer_name variable so invalid usernames (uppercase,
underscores, dots, leading/trailing hyphens, or too long) are rejected at plan
time.

resource_type = "clusters"

# Service account names
sentinel_sa_name = "hyperfleet-sentinel-${var.developer_name}"
adapter_sa_name = "hyperfleet-adapter-${var.developer_name}"
sentinel_k8s_sa_name = "sentinel"
adapter_k8s_sa_name = "hyperfleet-adapter"
adapter_k8s_sa_name = "adapter"

# Dead letter queue
enable_dead_letter = var.enable_dead_letter
Expand Down
2 changes: 1 addition & 1 deletion terraform/modules/pubsub/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
locals {
topic_name = var.topic_name != "" ? var.topic_name : "${var.namespace}-${var.resource_type}"
subscription_name = var.subscription_name != "" ? var.subscription_name : "${var.namespace}-adapter"
subscription_name = var.subscription_name != "" ? var.subscription_name : "${local.topic_name}-sub"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -path "*/terraform/modules/pubsub/main.tf" -type f

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 115


🏁 Script executed:

cat -n ./terraform/modules/pubsub/main.tf | head -50

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 2231


🏁 Script executed:

rg -i "namespace.*adapter|adapter.*namespace" --type=tf

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 726


🏁 Script executed:

cat -n ./terraform/modules/pubsub/variables.tf | grep -A 5 -B 5 "namespace.*adapter"

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 394


🏁 Script executed:

rg "adapter" --type=tf -i | head -30

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 2515


🏁 Script executed:

git log --oneline -n 20 -- terraform/modules/pubsub/main.tf

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 102


🏁 Script executed:

git show 6c08132:terraform/modules/pubsub/main.tf | head -50

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 1881


🏁 Script executed:

git log --all --oneline -- terraform/modules/pubsub/main.tf | head -10

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 179


🏁 Script executed:

git show ec8f9c1:terraform/modules/pubsub/main.tf | grep -A 5 "subscription_name"

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 552


🏁 Script executed:

rg "hyperfleet-adapter|namespace.*adapter" --type=tf --type=yaml --type=json --type=md -i | grep -v ".git"

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 1068


🏁 Script executed:

cat -n terraform/README.md | grep -A 3 -B 3 "adapter"

Repository: openshift-hyperfleet/hyperfleet-infra

Length of output: 717


Breaking change confirmed: subscription naming convention has been updated without migration mechanism.

The subscription naming has changed from "${var.namespace}-adapter" to "${local.topic_name}-sub", which forces a new resource to be created. This will cause Terraform to plan the destruction of existing subscriptions and creation of new ones, potentially orphaning GCP resources.

To safely migrate existing deployments:

  • Add a Terraform moved block to map the old subscription address to the new one, preventing resource destruction
  • Update documentation (README.md) to reflect the new naming pattern
  • Provide clear guidance for users to run terraform state mv if using versions prior to Terraform 1.1.0

Without these steps, existing deployments will experience service disruption and resource waste.

🤖 Prompt for AI Agents
In terraform/modules/pubsub/main.tf around line 3, the subscription naming was
changed from "${var.namespace}-adapter" to "${local.topic_name}-sub" which will
force replacement of existing resources; add a terraform "moved" block in the
module's root module (or in module registry as appropriate) mapping the old
resource address to the new one to preserve state (e.g., map
module.<module_name>.google_pubsub_subscription.<old_name> to
module.<module_name>.google_pubsub_subscription.<new_name>), update README.md to
document the new naming pattern and include step-by-step migration instructions,
and add a note instructing operators using Terraform <1.1.0 to run "terraform
state mv" manually with the exact addresses shown in the moved block so existing
subscriptions are not destroyed and re-created.

dlq_topic_name = "${local.topic_name}-dlq"

common_labels = merge(var.labels, {
Expand Down
19 changes: 0 additions & 19 deletions terraform/modules/pubsub/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ output "subscription_id" {
value = google_pubsub_subscription.adapter.id
}

# =============================================================================
# Service Account Outputs
# =============================================================================
output "sentinel_service_account_email" {
description = "Email of the Sentinel GCP service account"
value = google_service_account.sentinel.email
}

output "adapter_service_account_email" {
description = "Email of the Adapter GCP service account"
value = google_service_account.adapter.email
}

# =============================================================================
# Helm Values Snippet
# =============================================================================
Expand All @@ -52,9 +39,6 @@ output "helm_values_snippet" {

# For Sentinel:
sentinel:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: ${google_service_account.sentinel.email}
broker:
type: googlepubsub
topic: ${google_pubsub_topic.events.name}
Expand All @@ -64,9 +48,6 @@ output "helm_values_snippet" {

# For Adapter:
hyperfleet-adapter:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: ${google_service_account.adapter.email}
broker:
type: googlepubsub
subscriptionId: ${google_pubsub_subscription.adapter.name}
Expand Down
40 changes: 8 additions & 32 deletions terraform/modules/pubsub/service_accounts.tf
Original file line number Diff line number Diff line change
@@ -1,61 +1,37 @@
# =============================================================================
# Sentinel Service Account (Publisher)
# =============================================================================
resource "google_service_account" "sentinel" {
account_id = var.sentinel_sa_name
display_name = "HyperFleet Sentinel"
description = "Service account for HyperFleet Sentinel to publish events to Pub/Sub"
project = var.project_id
}

# Grant Sentinel permission to publish to the events topic
resource "google_pubsub_topic_iam_member" "sentinel_publisher" {
topic = google_pubsub_topic.events.name
role = "roles/pubsub.publisher"
member = "serviceAccount:${google_service_account.sentinel.email}"
project = var.project_id

resource "google_pubsub_topic_iam_member" "sentinel_publisher_wif" {
topic = google_pubsub_topic.events.name
role = "roles/pubsub.publisher"
member = "principal://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/${var.namespace}/sa/${var.sentinel_k8s_sa_name}"
project = var.project_id
}

# Workload Identity binding for Sentinel
# Allows the Kubernetes service account to impersonate the GCP service account
resource "google_service_account_iam_member" "sentinel_workload_identity" {
service_account_id = google_service_account.sentinel.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[${var.namespace}/${var.sentinel_k8s_sa_name}]"
}

# =============================================================================
# Adapter Service Account (Subscriber)
# =============================================================================
resource "google_service_account" "adapter" {
account_id = var.adapter_sa_name
display_name = "HyperFleet Adapter"
description = "Service account for HyperFleet Adapter to consume events from Pub/Sub"
project = var.project_id
}

# Grant Adapter permission to subscribe to the adapter subscription
resource "google_pubsub_subscription_iam_member" "adapter_subscriber" {
subscription = google_pubsub_subscription.adapter.name
role = "roles/pubsub.subscriber"
member = "serviceAccount:${google_service_account.adapter.email}"
member = "principal://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/${var.namespace}/sa/${var.adapter_k8s_sa_name}"
project = var.project_id
}

# Grant Adapter permission to view subscription (needed for some operations)
resource "google_pubsub_subscription_iam_member" "adapter_viewer" {
subscription = google_pubsub_subscription.adapter.name
role = "roles/pubsub.viewer"
member = "serviceAccount:${google_service_account.adapter.email}"
member = "principal://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/${var.namespace}/sa/${var.adapter_k8s_sa_name}"
project = var.project_id
}

# Workload Identity binding for Adapter
resource "google_service_account_iam_member" "adapter_workload_identity" {
service_account_id = google_service_account.adapter.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[${var.namespace}/${var.adapter_k8s_sa_name}]"
}

# =============================================================================
# Dead Letter Queue Permissions (if enabled)
Expand Down
12 changes: 0 additions & 12 deletions terraform/modules/pubsub/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,6 @@ variable "max_delivery_attempts" {
}
}

variable "sentinel_sa_name" {
description = "Sentinel GCP service account name"
type = string
default = "hyperfleet-sentinel"
}

variable "adapter_sa_name" {
description = "Adapter GCP service account name"
type = string
default = "hyperfleet-adapter"
}

variable "sentinel_k8s_sa_name" {
description = "Sentinel Kubernetes service account name"
type = string
Expand Down
14 changes: 0 additions & 14 deletions terraform/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,6 @@ output "connect_command" {
)
}

# =============================================================================
# Pub/Sub Outputs (when enabled)
# =============================================================================

output "sentinel_service_account_email" {
description = "Email of the Sentinel GCP service account"
value = var.enable_pubsub ? module.pubsub[0].sentinel_service_account_email : ""
}

output "adapter_service_account_email" {
description = "Email of the Adapter GCP service account"
value = var.enable_pubsub ? module.pubsub[0].adapter_service_account_email : ""
}

output "topic_name" {
description = "Name of the Pub/Sub topic"
value = var.enable_pubsub ? module.pubsub[0].topic_name : ""
Expand Down
16 changes: 1 addition & 15 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,13 @@ variable "aws_region" {
default = "us-east-1"
}

# =============================================================================
# Kubernetes Configuration
# =============================================================================
variable "kubernetes_namespace" {
description = "Kubernetes namespace for HyperFleet components"
type = string
default = "hyperfleet-system"

validation {
condition = length(var.kubernetes_namespace) > 0
error_message = "kubernetes_namespace must not be empty."
}
}

# =============================================================================
# Pub/Sub Configuration
# =============================================================================
variable "enable_pubsub" {
description = "Enable Google Pub/Sub for HyperFleet messaging"
type = bool
default = false
default = true
}

variable "enable_dead_letter" {
Expand Down