From 4ba2510f88b8775501099a943df5e1082cf34815 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 04:59:04 +0000 Subject: [PATCH 01/33] docs: Add global OTEL extension and backend service to Argo template --- README.md | 5 +++ argocd.yaml.tpl | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/README.md b/README.md index a0dffa2..d555c8f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_tenant_key` with your tenant/company key. Example: `antoniostacos` - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. + - Global OTEL extension placeholders: + - Replace `placeholder_otel_extension_version` with the extension release tag (example: `v0.1.0`). + - Replace `placeholder_otel_backend_tag` with the backend API image tag. + - Replace `placeholder_tempo_base_url` with your in-cluster Tempo endpoint. + - The OTEL extension is defined in `argocd.yaml` and loaded by ArgoCD itself, so it is global for all Argo applications without changing app templates. - Install ArgoCD diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 0c915a7..0fc8267 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -165,6 +165,7 @@ applicationSet: configs: params: server.insecure: true + server.enable.proxy.extension: true cm: # @ignored timeout.reconciliation: 10s @@ -217,6 +218,12 @@ configs: clientID: argocd clientSecret: placeholder_argocd_oidc_client_secret_from_dex redirectURI: https://argocd.placeholder_cluster_environment.placeholder_tenant_key.placeholder_glueops_root_domain/api/dex/callback + extension.config: | + extensions: + - name: otel-extension + backend: + services: + - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 rbac: # -- A good reference for this is: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/ # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. @@ -224,8 +231,21 @@ configs: # @default -- `''` (See [values.yaml]) policy.csv: | placeholder_argocd_rbac_policies + p, role:readonly, extensions, invoke, otel-extension, allow + p, role:admin, extensions, invoke, otel-extension, allow # @ignored server: + extensions: + enabled: true + extensionList: + - name: otel-extension + env: + - name: EXTENSION_URL + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" + - name: EXTENSION_VERSION + value: "placeholder_otel_extension_version" + - name: EXTENSION_ENABLED + value: "true" # @ignored affinity: nodeAffinity: @@ -297,6 +317,84 @@ extraObjects: port: number: 80 + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: otel-extension-api + namespace: glueops-core + labels: + app.kubernetes.io/name: otel-extension-api + spec: + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/name: otel-extension-api + template: + metadata: + labels: + app.kubernetes.io/name: otel-extension-api + spec: + nodeSelector: + glueops.dev/role: glueops-platform + tolerations: + - key: "glueops.dev/role" + operator: "Equal" + value: "glueops-platform" + effect: "NoSchedule" + containers: + - name: otel-extension-api + image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: PORT + value: "8000" + - name: PROMETHEUS_BASE_URL + value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" + - name: TEMPO_BASE_URL + value: "placeholder_tempo_base_url" + - name: LOG_LEVEL + value: "INFO" + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 20 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi + + - apiVersion: v1 + kind: Service + metadata: + name: otel-extension-api + namespace: glueops-core + labels: + app.kubernetes.io/name: otel-extension-api + spec: + type: ClusterIP + selector: + app.kubernetes.io/name: otel-extension-api + ports: + - name: http + port: 8000 + targetPort: http + protocol: TCP + - apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: From 0589ad9ca3671f90af56da3870816abd7e81f0d1 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 05:37:16 +0000 Subject: [PATCH 02/33] fix: update OTEL extension version and backend image tag to v0.1.1 --- argocd.yaml.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 0fc8267..06e7ef2 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -241,9 +241,9 @@ server: - name: otel-extension env: - name: EXTENSION_URL - value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/v0.1.1/extension.tar.gz" - name: EXTENSION_VERSION - value: "placeholder_otel_extension_version" + value: "0.1.1" - name: EXTENSION_ENABLED value: "true" # @ignored @@ -343,7 +343,7 @@ extraObjects: effect: "NoSchedule" containers: - name: otel-extension-api - image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" + image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:v0.1.1" imagePullPolicy: IfNotPresent ports: - name: http From 7e3ec12764e3aa20ffad172a3551c92080db1e48 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 06:01:00 +0000 Subject: [PATCH 03/33] feat: add global OTEL extension configuration and variables for ArgoCD --- README.md | 13 +++-- argocd.yaml.tpl | 14 +++--- main.tf | 125 ++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 127 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d555c8f..1fdd083 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,11 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_tenant_key` with your tenant/company key. Example: `antoniostacos` - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - - Global OTEL extension placeholders: - - Replace `placeholder_otel_extension_version` with the extension release tag (example: `v0.1.0`). - - Replace `placeholder_otel_backend_tag` with the backend API image tag. - - Replace `placeholder_tempo_base_url` with your in-cluster Tempo endpoint. + - OTEL is tenant-overridable through the Terraform module inputs: + - `otel_enabled` enables or disables the global ArgoCD OTEL extension for the tenant. + - `otel_extension_version` sets the GitHub release tag used for the extension tarball. + - `otel_backend_tag` sets the OTEL backend API image tag. + - `tempo_base_url` sets the in-cluster Tempo endpoint. Leave it empty to disable traces while keeping metrics enabled. - The OTEL extension is defined in `argocd.yaml` and loaded by ArgoCD itself, so it is global for all Argo applications without changing app templates. - Install ArgoCD @@ -48,6 +49,10 @@ module "argocd_helm_values" { cluster_environment = "nonprod" client_secret = "Zsbui/29YEqoGOzuI8snlqGcdaRYPSLocwLXDB5GhZY=" glueops_root_domain = "onglueops.com" + otel_enabled = true + otel_extension_version = "v0.1.1" + otel_backend_tag = "v0.1.1" + tempo_base_url = "http://tempo.glueops-core-tempo.svc.cluster.local:3200" } output "argocd_helm_values" { diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 06e7ef2..05a46cb 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -165,7 +165,7 @@ applicationSet: configs: params: server.insecure: true - server.enable.proxy.extension: true + server.enable.proxy.extension: placeholder_otel_enabled cm: # @ignored timeout.reconciliation: 10s @@ -236,16 +236,16 @@ configs: # @ignored server: extensions: - enabled: true + enabled: placeholder_otel_enabled extensionList: - name: otel-extension env: - name: EXTENSION_URL - value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/v0.1.1/extension.tar.gz" + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" - name: EXTENSION_VERSION - value: "0.1.1" + value: "placeholder_otel_extension_semver" - name: EXTENSION_ENABLED - value: "true" + value: "placeholder_otel_enabled" # @ignored affinity: nodeAffinity: @@ -325,7 +325,7 @@ extraObjects: labels: app.kubernetes.io/name: otel-extension-api spec: - replicas: 2 + replicas: placeholder_otel_backend_replicas selector: matchLabels: app.kubernetes.io/name: otel-extension-api @@ -343,7 +343,7 @@ extraObjects: effect: "NoSchedule" containers: - name: otel-extension-api - image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:v0.1.1" + image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" imagePullPolicy: IfNotPresent ports: - name: http diff --git a/main.tf b/main.tf index 2dbf4d7..124556f 100644 --- a/main.tf +++ b/main.tf @@ -1,10 +1,10 @@ terraform { required_providers { http = { - source = "hashicorp/http" + source = "hashicorp/http" } local = { - source = "hashicorp/local" + source = "hashicorp/local" } } } @@ -56,18 +56,115 @@ variable "gatekeeper_tag" { description = "Image tag (SHA or semver) for ghcr.repo.gpkg.io/glueops/gatekeeper.platform.glueops.dev" } +variable "otel_enabled" { + type = bool + description = "Enable or disable the global ArgoCD OTEL extension and its backend service for this tenant" + default = true +} -output "helm_values" { - value = replace(replace(replace(replace(replace( - replace( - replace( - data.local_file.argocd_template.content, - "placeholder_tenant_key", var.tenant_key), - "placeholder_cluster_environment", var.cluster_environment), - "placeholder_argocd_oidc_client_secret_from_dex", var.client_secret), - "placeholder_glueops_root_domain", var.glueops_root_domain), - " placeholder_argocd_rbac_policies", var.argocd_rbac_policies), - "placeholder_argocd_app_version", var.argocd_app_version), - "placeholder_gatekeeper_tag", var.gatekeeper_tag +variable "otel_extension_version" { + type = string + description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.1)" + default = "v0.1.1" +} + +variable "otel_backend_tag" { + type = string + description = "Image tag (SHA or semver) for ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api" + default = "v0.1.1" +} + +variable "tempo_base_url" { + type = string + description = "In-cluster Tempo base URL for trace search. Leave empty to disable traces while keeping metrics enabled." + default = "" +} + +locals { + otel_enabled_string = var.otel_enabled ? "true" : "false" + otel_backend_replicas = var.otel_enabled ? "2" : "0" + otel_extension_semver = trimprefix(var.otel_extension_version, "v") + + rendered_argocd_values_tenant = replace( + data.local_file.argocd_template.content, + "placeholder_tenant_key", + var.tenant_key + ) + + rendered_argocd_values_environment = replace( + local.rendered_argocd_values_tenant, + "placeholder_cluster_environment", + var.cluster_environment + ) + + rendered_argocd_values_secret = replace( + local.rendered_argocd_values_environment, + "placeholder_argocd_oidc_client_secret_from_dex", + var.client_secret + ) + + rendered_argocd_values_domain = replace( + local.rendered_argocd_values_secret, + "placeholder_glueops_root_domain", + var.glueops_root_domain + ) + + rendered_argocd_values_rbac = replace( + local.rendered_argocd_values_domain, + " placeholder_argocd_rbac_policies", + var.argocd_rbac_policies ) + + rendered_argocd_values_app_version = replace( + local.rendered_argocd_values_rbac, + "placeholder_argocd_app_version", + var.argocd_app_version + ) + + rendered_argocd_values_gatekeeper = replace( + local.rendered_argocd_values_app_version, + "placeholder_gatekeeper_tag", + var.gatekeeper_tag + ) + + rendered_argocd_values_otel_enabled = replace( + local.rendered_argocd_values_gatekeeper, + "placeholder_otel_enabled", + local.otel_enabled_string + ) + + rendered_argocd_values_otel_replicas = replace( + local.rendered_argocd_values_otel_enabled, + "placeholder_otel_backend_replicas", + local.otel_backend_replicas + ) + + rendered_argocd_values_otel_version = replace( + local.rendered_argocd_values_otel_replicas, + "placeholder_otel_extension_version", + var.otel_extension_version + ) + + rendered_argocd_values_otel_semver = replace( + local.rendered_argocd_values_otel_version, + "placeholder_otel_extension_semver", + local.otel_extension_semver + ) + + rendered_argocd_values_otel_backend_tag = replace( + local.rendered_argocd_values_otel_semver, + "placeholder_otel_backend_tag", + var.otel_backend_tag + ) + + rendered_argocd_values = replace( + local.rendered_argocd_values_otel_backend_tag, + "placeholder_tempo_base_url", + var.tempo_base_url + ) +} + + +output "helm_values" { + value = local.rendered_argocd_values } From 375ebf0529729bf0e6bd0b574d079269f4a2a708 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 07:03:37 +0000 Subject: [PATCH 04/33] feat: update global OTEL extension configuration to be tenant-overridable --- README.md | 2 +- argocd.yaml.tpl | 101 ++-------------------------------- main.tf | 142 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index 1fdd083..bbe7b70 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - OTEL is tenant-overridable through the Terraform module inputs: - - `otel_enabled` enables or disables the global ArgoCD OTEL extension for the tenant. + - `otel_enabled` enables or disables the global ArgoCD OTEL extension for the tenant. The default is `false`, so tenants must opt in explicitly. - `otel_extension_version` sets the GitHub release tag used for the extension tarball. - `otel_backend_tag` sets the OTEL backend API image tag. - `tempo_base_url` sets the in-cluster Tempo endpoint. Leave it empty to disable traces while keeping metrics enabled. diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 05a46cb..816cbca 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -218,12 +218,7 @@ configs: clientID: argocd clientSecret: placeholder_argocd_oidc_client_secret_from_dex redirectURI: https://argocd.placeholder_cluster_environment.placeholder_tenant_key.placeholder_glueops_root_domain/api/dex/callback - extension.config: | - extensions: - - name: otel-extension - backend: - services: - - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 +placeholder_otel_extension_config rbac: # -- A good reference for this is: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/ # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. @@ -231,21 +226,10 @@ configs: # @default -- `''` (See [values.yaml]) policy.csv: | placeholder_argocd_rbac_policies - p, role:readonly, extensions, invoke, otel-extension, allow - p, role:admin, extensions, invoke, otel-extension, allow +placeholder_otel_rbac_policies # @ignored server: - extensions: - enabled: placeholder_otel_enabled - extensionList: - - name: otel-extension - env: - - name: EXTENSION_URL - value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" - - name: EXTENSION_VERSION - value: "placeholder_otel_extension_semver" - - name: EXTENSION_ENABLED - value: "placeholder_otel_enabled" +placeholder_otel_server_extensions # @ignored affinity: nodeAffinity: @@ -316,84 +300,7 @@ extraObjects: name: argocd-server port: number: 80 - - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - replicas: placeholder_otel_backend_replicas - selector: - matchLabels: - app.kubernetes.io/name: otel-extension-api - template: - metadata: - labels: - app.kubernetes.io/name: otel-extension-api - spec: - nodeSelector: - glueops.dev/role: glueops-platform - tolerations: - - key: "glueops.dev/role" - operator: "Equal" - value: "glueops-platform" - effect: "NoSchedule" - containers: - - name: otel-extension-api - image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8000 - protocol: TCP - env: - - name: PORT - value: "8000" - - name: PROMETHEUS_BASE_URL - value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" - - name: TEMPO_BASE_URL - value: "placeholder_tempo_base_url" - - name: LOG_LEVEL - value: "INFO" - readinessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 15 - periodSeconds: 20 - resources: - requests: - cpu: 50m - memory: 64Mi - limits: - cpu: 250m - memory: 256Mi - - - apiVersion: v1 - kind: Service - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - type: ClusterIP - selector: - app.kubernetes.io/name: otel-extension-api - ports: - - name: http - port: 8000 - targetPort: http - protocol: TCP +placeholder_otel_backend_objects - apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/main.tf b/main.tf index 124556f..b7d2bd7 100644 --- a/main.tf +++ b/main.tf @@ -59,7 +59,7 @@ variable "gatekeeper_tag" { variable "otel_enabled" { type = bool description = "Enable or disable the global ArgoCD OTEL extension and its backend service for this tenant" - default = true + default = false } variable "otel_extension_version" { @@ -82,8 +82,120 @@ variable "tempo_base_url" { locals { otel_enabled_string = var.otel_enabled ? "true" : "false" - otel_backend_replicas = var.otel_enabled ? "2" : "0" otel_extension_semver = trimprefix(var.otel_extension_version, "v") + otel_extension_config = var.otel_enabled ? ( + <<-EOT + extension.config: | + extensions: + - name: otel-extension + backend: + services: + - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 + EOT + ) : "" + otel_rbac_policies = var.otel_enabled ? ( + <<-EOT + p, role:readonly, extensions, invoke, otel-extension, allow + p, role:admin, extensions, invoke, otel-extension, allow + EOT + ) : "" + otel_server_extensions = var.otel_enabled ? ( + <<-EOT + extensions: + enabled: true + extensionList: + - name: otel-extension + env: + - name: EXTENSION_URL + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" + - name: EXTENSION_VERSION + value: "placeholder_otel_extension_semver" + - name: EXTENSION_ENABLED + value: "true" + EOT + ) : "" + otel_backend_objects = var.otel_enabled ? ( + <<-EOT + + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: otel-extension-api + namespace: glueops-core + labels: + app.kubernetes.io/name: otel-extension-api + spec: + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/name: otel-extension-api + template: + metadata: + labels: + app.kubernetes.io/name: otel-extension-api + spec: + nodeSelector: + glueops.dev/role: glueops-platform + tolerations: + - key: "glueops.dev/role" + operator: "Equal" + value: "glueops-platform" + effect: "NoSchedule" + containers: + - name: otel-extension-api + image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: PORT + value: "8000" + - name: PROMETHEUS_BASE_URL + value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" + - name: TEMPO_BASE_URL + value: "placeholder_tempo_base_url" + - name: LOG_LEVEL + value: "INFO" + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 20 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi + + - apiVersion: v1 + kind: Service + metadata: + name: otel-extension-api + namespace: glueops-core + labels: + app.kubernetes.io/name: otel-extension-api + spec: + type: ClusterIP + selector: + app.kubernetes.io/name: otel-extension-api + ports: + - name: http + port: 8000 + targetPort: http + protocol: TCP + EOT + ) : "" rendered_argocd_values_tenant = replace( data.local_file.argocd_template.content, @@ -133,14 +245,32 @@ locals { local.otel_enabled_string ) - rendered_argocd_values_otel_replicas = replace( + rendered_argocd_values_otel_extension_config = replace( local.rendered_argocd_values_otel_enabled, - "placeholder_otel_backend_replicas", - local.otel_backend_replicas + "placeholder_otel_extension_config", + local.otel_extension_config + ) + + rendered_argocd_values_otel_rbac = replace( + local.rendered_argocd_values_otel_extension_config, + "placeholder_otel_rbac_policies", + local.otel_rbac_policies + ) + + rendered_argocd_values_otel_server_extensions = replace( + local.rendered_argocd_values_otel_rbac, + "placeholder_otel_server_extensions", + local.otel_server_extensions + ) + + rendered_argocd_values_otel_backend_objects = replace( + local.rendered_argocd_values_otel_server_extensions, + "placeholder_otel_backend_objects", + local.otel_backend_objects ) rendered_argocd_values_otel_version = replace( - local.rendered_argocd_values_otel_replicas, + local.rendered_argocd_values_otel_backend_objects, "placeholder_otel_extension_version", var.otel_extension_version ) From cf3c6cfdf78dcae73496d1e1a2b3fc3aad7c4ad6 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 07:16:44 +0000 Subject: [PATCH 05/33] feat: update placeholder comments for OTEL extension configurations in ArgoCD templates --- argocd.yaml.tpl | 8 ++++---- main.tf | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 816cbca..b9d43ce 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -218,7 +218,7 @@ configs: clientID: argocd clientSecret: placeholder_argocd_oidc_client_secret_from_dex redirectURI: https://argocd.placeholder_cluster_environment.placeholder_tenant_key.placeholder_glueops_root_domain/api/dex/callback -placeholder_otel_extension_config +# placeholder_otel_extension_config rbac: # -- A good reference for this is: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/ # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. @@ -226,10 +226,10 @@ placeholder_otel_extension_config # @default -- `''` (See [values.yaml]) policy.csv: | placeholder_argocd_rbac_policies -placeholder_otel_rbac_policies +# placeholder_otel_rbac_policies # @ignored server: -placeholder_otel_server_extensions +# placeholder_otel_server_extensions # @ignored affinity: nodeAffinity: @@ -300,7 +300,7 @@ extraObjects: name: argocd-server port: number: 80 -placeholder_otel_backend_objects +# placeholder_otel_backend_objects - apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/main.tf b/main.tf index b7d2bd7..bcc1ba3 100644 --- a/main.tf +++ b/main.tf @@ -247,25 +247,25 @@ locals { rendered_argocd_values_otel_extension_config = replace( local.rendered_argocd_values_otel_enabled, - "placeholder_otel_extension_config", + "# placeholder_otel_extension_config", local.otel_extension_config ) rendered_argocd_values_otel_rbac = replace( local.rendered_argocd_values_otel_extension_config, - "placeholder_otel_rbac_policies", + "# placeholder_otel_rbac_policies", local.otel_rbac_policies ) rendered_argocd_values_otel_server_extensions = replace( local.rendered_argocd_values_otel_rbac, - "placeholder_otel_server_extensions", + "# placeholder_otel_server_extensions", local.otel_server_extensions ) rendered_argocd_values_otel_backend_objects = replace( local.rendered_argocd_values_otel_server_extensions, - "placeholder_otel_backend_objects", + "# placeholder_otel_backend_objects", local.otel_backend_objects ) From 0a309d16965080c7990b24708042ad794e6e38fc Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 15 Jun 2026 10:03:09 +0000 Subject: [PATCH 06/33] feat: refine OTEL extension configuration and improve formatting in ArgoCD templates --- argocd.yaml.tpl | 6 +- main.tf | 190 ++++++++++++++++++++++++------------------------ 2 files changed, 98 insertions(+), 98 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index b9d43ce..8c626b7 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -218,7 +218,7 @@ configs: clientID: argocd clientSecret: placeholder_argocd_oidc_client_secret_from_dex redirectURI: https://argocd.placeholder_cluster_environment.placeholder_tenant_key.placeholder_glueops_root_domain/api/dex/callback -# placeholder_otel_extension_config + # placeholder_otel_extension_config rbac: # -- A good reference for this is: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/ # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. @@ -226,10 +226,10 @@ configs: # @default -- `''` (See [values.yaml]) policy.csv: | placeholder_argocd_rbac_policies -# placeholder_otel_rbac_policies + # placeholder_otel_rbac_policies # @ignored server: -# placeholder_otel_server_extensions + # placeholder_otel_server_extensions # @ignored affinity: nodeAffinity: diff --git a/main.tf b/main.tf index bcc1ba3..fd0c13f 100644 --- a/main.tf +++ b/main.tf @@ -85,115 +85,115 @@ locals { otel_extension_semver = trimprefix(var.otel_extension_version, "v") otel_extension_config = var.otel_enabled ? ( <<-EOT - extension.config: | - extensions: - - name: otel-extension - backend: - services: - - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 + extension.config: | + extensions: + - name: otel-extension + backend: + services: + - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 EOT ) : "" otel_rbac_policies = var.otel_enabled ? ( <<-EOT - p, role:readonly, extensions, invoke, otel-extension, allow - p, role:admin, extensions, invoke, otel-extension, allow + p, role:readonly, extensions, invoke, otel-extension, allow + p, role:admin, extensions, invoke, otel-extension, allow EOT ) : "" otel_server_extensions = var.otel_enabled ? ( <<-EOT - extensions: - enabled: true - extensionList: - - name: otel-extension - env: - - name: EXTENSION_URL - value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" - - name: EXTENSION_VERSION - value: "placeholder_otel_extension_semver" - - name: EXTENSION_ENABLED - value: "true" + extensions: + enabled: true + extensionList: + - name: otel-extension + env: + - name: EXTENSION_URL + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" + - name: EXTENSION_VERSION + value: "placeholder_otel_extension_semver" + - name: EXTENSION_ENABLED + value: "true" EOT ) : "" otel_backend_objects = var.otel_enabled ? ( <<-EOT - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - replicas: 2 - selector: - matchLabels: - app.kubernetes.io/name: otel-extension-api - template: + - apiVersion: apps/v1 + kind: Deployment metadata: + name: otel-extension-api + namespace: glueops-core labels: app.kubernetes.io/name: otel-extension-api spec: - nodeSelector: - glueops.dev/role: glueops-platform - tolerations: - - key: "glueops.dev/role" - operator: "Equal" - value: "glueops-platform" - effect: "NoSchedule" - containers: - - name: otel-extension-api - image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8000 - protocol: TCP - env: - - name: PORT - value: "8000" - - name: PROMETHEUS_BASE_URL - value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" - - name: TEMPO_BASE_URL - value: "placeholder_tempo_base_url" - - name: LOG_LEVEL - value: "INFO" - readinessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 15 - periodSeconds: 20 - resources: - requests: - cpu: 50m - memory: 64Mi - limits: - cpu: 250m - memory: 256Mi - - - apiVersion: v1 - kind: Service - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - type: ClusterIP - selector: - app.kubernetes.io/name: otel-extension-api - ports: - - name: http - port: 8000 - targetPort: http - protocol: TCP + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/name: otel-extension-api + template: + metadata: + labels: + app.kubernetes.io/name: otel-extension-api + spec: + nodeSelector: + glueops.dev/role: glueops-platform + tolerations: + - key: "glueops.dev/role" + operator: "Equal" + value: "glueops-platform" + effect: "NoSchedule" + containers: + - name: otel-extension-api + image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: PORT + value: "8000" + - name: PROMETHEUS_BASE_URL + value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" + - name: TEMPO_BASE_URL + value: "placeholder_tempo_base_url" + - name: LOG_LEVEL + value: "INFO" + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 20 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi + + - apiVersion: v1 + kind: Service + metadata: + name: otel-extension-api + namespace: glueops-core + labels: + app.kubernetes.io/name: otel-extension-api + spec: + type: ClusterIP + selector: + app.kubernetes.io/name: otel-extension-api + ports: + - name: http + port: 8000 + targetPort: http + protocol: TCP EOT ) : "" @@ -247,19 +247,19 @@ locals { rendered_argocd_values_otel_extension_config = replace( local.rendered_argocd_values_otel_enabled, - "# placeholder_otel_extension_config", + " # placeholder_otel_extension_config", local.otel_extension_config ) rendered_argocd_values_otel_rbac = replace( local.rendered_argocd_values_otel_extension_config, - "# placeholder_otel_rbac_policies", + " # placeholder_otel_rbac_policies", local.otel_rbac_policies ) rendered_argocd_values_otel_server_extensions = replace( local.rendered_argocd_values_otel_rbac, - "# placeholder_otel_server_extensions", + " # placeholder_otel_server_extensions", local.otel_server_extensions ) From 58de9b5f43ebc6f4df053bf7e8c2bf8ad4f51d1c Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Thu, 18 Jun 2026 07:56:51 +0000 Subject: [PATCH 07/33] feat: refactor OTEL extension configuration for improved readability and add preconditions for version variables --- main.tf | 228 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 115 insertions(+), 113 deletions(-) diff --git a/main.tf b/main.tf index fd0c13f..eb2ee53 100644 --- a/main.tf +++ b/main.tf @@ -83,119 +83,111 @@ variable "tempo_base_url" { locals { otel_enabled_string = var.otel_enabled ? "true" : "false" otel_extension_semver = trimprefix(var.otel_extension_version, "v") - otel_extension_config = var.otel_enabled ? ( - <<-EOT - extension.config: | - extensions: - - name: otel-extension - backend: - services: - - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000 - EOT - ) : "" - otel_rbac_policies = var.otel_enabled ? ( - <<-EOT - p, role:readonly, extensions, invoke, otel-extension, allow - p, role:admin, extensions, invoke, otel-extension, allow - EOT - ) : "" - otel_server_extensions = var.otel_enabled ? ( - <<-EOT - extensions: - enabled: true - extensionList: - - name: otel-extension - env: - - name: EXTENSION_URL - value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" - - name: EXTENSION_VERSION - value: "placeholder_otel_extension_semver" - - name: EXTENSION_ENABLED - value: "true" - EOT - ) : "" - otel_backend_objects = var.otel_enabled ? ( - <<-EOT - - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - replicas: 2 - selector: - matchLabels: - app.kubernetes.io/name: otel-extension-api - template: - metadata: - labels: - app.kubernetes.io/name: otel-extension-api - spec: - nodeSelector: - glueops.dev/role: glueops-platform - tolerations: - - key: "glueops.dev/role" - operator: "Equal" - value: "glueops-platform" - effect: "NoSchedule" - containers: - - name: otel-extension-api - image: "ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8000 - protocol: TCP - env: - - name: PORT - value: "8000" - - name: PROMETHEUS_BASE_URL - value: "http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090" - - name: TEMPO_BASE_URL - value: "placeholder_tempo_base_url" - - name: LOG_LEVEL - value: "INFO" - readinessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 15 - periodSeconds: 20 - resources: - requests: - cpu: 50m - memory: 64Mi - limits: - cpu: 250m - memory: 256Mi - - - apiVersion: v1 - kind: Service - metadata: - name: otel-extension-api - namespace: glueops-core - labels: - app.kubernetes.io/name: otel-extension-api - spec: - type: ClusterIP - selector: - app.kubernetes.io/name: otel-extension-api - ports: - - name: http - port: 8000 - targetPort: http - protocol: TCP - EOT - ) : "" + otel_extension_config = var.otel_enabled ? join("\n", [ + " extension.config: |", + " extensions:", + " - name: otel-extension", + " backend:", + " services:", + " - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000", + ]) : "" + otel_rbac_policies = var.otel_enabled ? join("\n", [ + " p, role:readonly, extensions, invoke, otel-extension, allow", + " p, role:admin, extensions, invoke, otel-extension, allow", + ]) : "" + otel_server_extensions = var.otel_enabled ? join("\n", [ + " extensions:", + " enabled: true", + " extensionList:", + " - name: otel-extension", + " env:", + " - name: EXTENSION_URL", + " value: \"https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz\"", + " - name: EXTENSION_VERSION", + " value: \"placeholder_otel_extension_semver\"", + " - name: EXTENSION_ENABLED", + " value: \"true\"", + ]) : "" + otel_backend_objects = var.otel_enabled ? join("\n", [ + "", + " - apiVersion: apps/v1", + " kind: Deployment", + " metadata:", + " name: otel-extension-api", + " namespace: glueops-core", + " labels:", + " app.kubernetes.io/name: otel-extension-api", + " spec:", + " replicas: 2", + " selector:", + " matchLabels:", + " app.kubernetes.io/name: otel-extension-api", + " template:", + " metadata:", + " labels:", + " app.kubernetes.io/name: otel-extension-api", + " spec:", + " nodeSelector:", + " glueops.dev/role: glueops-platform", + " tolerations:", + " - key: \"glueops.dev/role\"", + " operator: \"Equal\"", + " value: \"glueops-platform\"", + " effect: \"NoSchedule\"", + " containers:", + " - name: otel-extension-api", + " image: \"ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag\"", + " imagePullPolicy: IfNotPresent", + " ports:", + " - name: http", + " containerPort: 8000", + " protocol: TCP", + " env:", + " - name: PORT", + " value: \"8000\"", + " - name: PROMETHEUS_BASE_URL", + " value: \"http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090\"", + " - name: TEMPO_BASE_URL", + " value: \"placeholder_tempo_base_url\"", + " - name: LOG_LEVEL", + " value: \"INFO\"", + " readinessProbe:", + " httpGet:", + " path: /healthz", + " port: http", + " initialDelaySeconds: 5", + " periodSeconds: 10", + " livenessProbe:", + " httpGet:", + " path: /healthz", + " port: http", + " initialDelaySeconds: 15", + " periodSeconds: 20", + " resources:", + " requests:", + " cpu: 50m", + " memory: 64Mi", + " limits:", + " cpu: 250m", + " memory: 256Mi", + "", + " - apiVersion: v1", + " kind: Service", + " metadata:", + " name: otel-extension-api", + " namespace: glueops-core", + " labels:", + " app.kubernetes.io/name: otel-extension-api", + " spec:", + " type: ClusterIP", + " selector:", + " app.kubernetes.io/name: otel-extension-api", + " ports:", + " - name: http", + " port: 8000", + " targetPort: http", + " protocol: TCP", + ]) : "" rendered_argocd_values_tenant = replace( data.local_file.argocd_template.content, @@ -297,4 +289,14 @@ locals { output "helm_values" { value = local.rendered_argocd_values + + precondition { + condition = !var.otel_enabled || trimspace(var.otel_extension_version) != "" + error_message = "otel_extension_version must be non-empty when otel_enabled is true" + } + + precondition { + condition = !var.otel_enabled || trimspace(var.otel_backend_tag) != "" + error_message = "otel_backend_tag must be non-empty when otel_enabled is true" + } } From d5406c8f447c8f0969ce18a34b202633c209ca45 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Thu, 18 Jun 2026 08:03:35 +0000 Subject: [PATCH 08/33] feat: update required Terraform version in main.tf for compatibility --- main.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.tf b/main.tf index eb2ee53..d9910d0 100644 --- a/main.tf +++ b/main.tf @@ -1,4 +1,6 @@ terraform { + required_version = ">= 1.2.0" + required_providers { http = { source = "hashicorp/http" From aca9d8e207206d099ba75efbc888be132451421d Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Thu, 18 Jun 2026 08:22:31 +0000 Subject: [PATCH 09/33] feat: trim whitespace from OTEL extension version and backend tag variables in locals --- main.tf | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/main.tf b/main.tf index d9910d0..c0b9dd3 100644 --- a/main.tf +++ b/main.tf @@ -84,7 +84,10 @@ variable "tempo_base_url" { locals { otel_enabled_string = var.otel_enabled ? "true" : "false" - otel_extension_semver = trimprefix(var.otel_extension_version, "v") + otel_extension_version_trimmed = trimspace(var.otel_extension_version) + otel_backend_tag_trimmed = trimspace(var.otel_backend_tag) + tempo_base_url_trimmed = trimspace(var.tempo_base_url) + otel_extension_semver = trimprefix(local.otel_extension_version_trimmed, "v") otel_extension_config = var.otel_enabled ? join("\n", [ " extension.config: |", " extensions:", @@ -266,7 +269,7 @@ locals { rendered_argocd_values_otel_version = replace( local.rendered_argocd_values_otel_backend_objects, "placeholder_otel_extension_version", - var.otel_extension_version + local.otel_extension_version_trimmed ) rendered_argocd_values_otel_semver = replace( @@ -278,13 +281,13 @@ locals { rendered_argocd_values_otel_backend_tag = replace( local.rendered_argocd_values_otel_semver, "placeholder_otel_backend_tag", - var.otel_backend_tag + local.otel_backend_tag_trimmed ) rendered_argocd_values = replace( local.rendered_argocd_values_otel_backend_tag, "placeholder_tempo_base_url", - var.tempo_base_url + local.tempo_base_url_trimmed ) } From ba63f8f48bc1d72744c5de3cfd4ffae8bf05bf28 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Thu, 18 Jun 2026 08:29:19 +0000 Subject: [PATCH 10/33] feat: add instructions for installing OTEL directly from argocd.yaml.tpl --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bbe7b70..43775ca 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - `otel_extension_version` sets the GitHub release tag used for the extension tarball. - `otel_backend_tag` sets the OTEL backend API image tag. - `tempo_base_url` sets the in-cluster Tempo endpoint. Leave it empty to disable traces while keeping metrics enabled. + - If you are installing from the downloaded `argocd.yaml.tpl` directly instead of using Terraform, replace `placeholder_otel_enabled` with `true` or `false` before running Helm. Leave the OTEL placeholder comments in place to keep OTEL disabled, or replace those placeholder comments with concrete OTEL config, RBAC, server extension, and backend object blocks if you want OTEL enabled without Terraform. - The OTEL extension is defined in `argocd.yaml` and loaded by ArgoCD itself, so it is global for all Argo applications without changing app templates. - Install ArgoCD From ed14832e19b2cddb3f3e0149be49189bc0384f7b Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Thu, 18 Jun 2026 08:34:31 +0000 Subject: [PATCH 11/33] feat: add preconditions for OTEL extension variables to prevent whitespace --- main.tf | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/main.tf b/main.tf index c0b9dd3..991c36b 100644 --- a/main.tf +++ b/main.tf @@ -300,8 +300,23 @@ output "helm_values" { error_message = "otel_extension_version must be non-empty when otel_enabled is true" } + precondition { + condition = !var.otel_enabled || length(regexall("\\s", local.otel_extension_version_trimmed)) == 0 + error_message = "otel_extension_version must not contain whitespace when otel_enabled is true" + } + precondition { condition = !var.otel_enabled || trimspace(var.otel_backend_tag) != "" error_message = "otel_backend_tag must be non-empty when otel_enabled is true" } + + precondition { + condition = !var.otel_enabled || length(regexall("\\s", local.otel_backend_tag_trimmed)) == 0 + error_message = "otel_backend_tag must not contain whitespace when otel_enabled is true" + } + + precondition { + condition = !var.otel_enabled || local.tempo_base_url_trimmed == "" || length(regexall("\\s", local.tempo_base_url_trimmed)) == 0 + error_message = "tempo_base_url must not contain whitespace when otel_enabled is true" + } } From 9b85769d0918ac00df63d20697b7dc2d2adcff5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Jun 2026 05:15:30 +0000 Subject: [PATCH 12/33] docs: fix README OTEL usage examples --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 43775ca..6386eec 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - `otel_extension_version` sets the GitHub release tag used for the extension tarball. - `otel_backend_tag` sets the OTEL backend API image tag. - `tempo_base_url` sets the in-cluster Tempo endpoint. Leave it empty to disable traces while keeping metrics enabled. - - If you are installing from the downloaded `argocd.yaml.tpl` directly instead of using Terraform, replace `placeholder_otel_enabled` with `true` or `false` before running Helm. Leave the OTEL placeholder comments in place to keep OTEL disabled, or replace those placeholder comments with concrete OTEL config, RBAC, server extension, and backend object blocks if you want OTEL enabled without Terraform. + - If you are installing from the downloaded `argocd.yaml` directly instead of using Terraform, replace `placeholder_otel_enabled` with `true` or `false` before running Helm. Leave the OTEL placeholder comments in place to keep OTEL disabled, or replace those placeholder comments with concrete OTEL config, RBAC, server extension, and backend object blocks if you want OTEL enabled without Terraform. - The OTEL extension is defined in `argocd.yaml` and loaded by ArgoCD itself, so it is global for all Argo applications without changing app templates. - Install ArgoCD @@ -45,18 +45,20 @@ kubectl get pods -n glueops-core ```hcl module "argocd_helm_values" { - source = "git::https://github.com/GlueOps/docs-argocd.git" - tenant_key = "antoniostacos" - cluster_environment = "nonprod" - client_secret = "Zsbui/29YEqoGOzuI8snlqGcdaRYPSLocwLXDB5GhZY=" - glueops_root_domain = "onglueops.com" - otel_enabled = true - otel_extension_version = "v0.1.1" - otel_backend_tag = "v0.1.1" - tempo_base_url = "http://tempo.glueops-core-tempo.svc.cluster.local:3200" + source = "git::https://github.com/GlueOps/docs-argocd.git" + tenant_key = "antoniostacos" + cluster_environment = "nonprod" + client_secret = "Zsbui/29YEqoGOzuI8snlqGcdaRYPSLocwLXDB5GhZY=" + glueops_root_domain = "onglueops.com" + argocd_app_version = "v2.8.6" + gatekeeper_tag = "v1.0.0" + otel_enabled = true + otel_extension_version = "v0.1.1" + otel_backend_tag = "v0.1.1" + tempo_base_url = "http://tempo.glueops-core-tempo.svc.cluster.local:3200" } output "argocd_helm_values" { - value = module.argocd_yaml.argocd + value = module.argocd_helm_values.helm_values } ``` From fabab4dd6f82dad154e5538248de15c6e446d80d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Jun 2026 05:43:03 +0000 Subject: [PATCH 13/33] feat: add otel-extension-api backend service and release workflow --- .github/workflows/release.yml | 42 ++ .gitignore | 1 + backend/.dockerignore | 3 + backend/Dockerfile | 8 + backend/README.md | 46 ++ backend/package-lock.json | 1041 +++++++++++++++++++++++++++++++++ backend/package.json | 16 + backend/src/server.js | 72 +++ 8 files changed, 1229 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 backend/README.md create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/src/server.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..929454a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Release + +on: + release: + types: [published] + +permissions: + contents: read + packages: write + +jobs: + build-and-push-backend: + name: Build and push backend image + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/glueops/argocd-otel-extension-api + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ./backend + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..baf1908 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +backend/node_modules/ diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..f4100b6 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +npm-debug.log +.npm diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..75399ff --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,8 @@ +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY src/ ./src/ +EXPOSE 8000 +USER node +CMD ["node", "src/server.js"] diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..058b098 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,46 @@ +# argocd-otel-extension-api + +Backend service for the ArgoCD OTEL UI extension. It proxies Prometheus and Tempo endpoints so the extension frontend can query metrics and traces without direct cluster network access. + +## Endpoints + +| Path | Proxied to | +|------|------------| +| `GET /healthz` | Local health check — returns `{"status":"ok"}` | +| `ANY /prometheus/*` | `PROMETHEUS_BASE_URL/*` (path prefix stripped) | +| `ANY /tempo/*` | `TEMPO_BASE_URL/*` (path prefix stripped) | + +### Prometheus example +``` +GET /prometheus/api/v1/query?query=up +``` +Proxied to `$PROMETHEUS_BASE_URL/api/v1/query?query=up`. + +### Tempo example +``` +GET /tempo/api/search?tags=service.name%3Dmyapp +``` +Proxied to `$TEMPO_BASE_URL/api/search?tags=service.name%3Dmyapp`. + +## Environment variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `PORT` | No | `8000` | Port the server listens on | +| `PROMETHEUS_BASE_URL` | Yes (for metrics) | `""` | In-cluster Prometheus base URL, e.g. `http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090` | +| `TEMPO_BASE_URL` | No | `""` | In-cluster Tempo base URL, e.g. `http://tempo.glueops-core-tempo.svc.cluster.local:3200`. Leave empty to disable trace proxying. | +| `LOG_LEVEL` | No | `INFO` | Log verbosity: `DEBUG`, `INFO`, `WARN`, or `ERROR` | + +## Running locally + +```bash +npm install +PROMETHEUS_BASE_URL=http://localhost:9090 node src/server.js +``` + +## Container image + +The image is published to GHCR on every release: +``` +ghcr.io/glueops/argocd-otel-extension-api: +``` diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..5254517 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,1041 @@ +{ + "name": "argocd-otel-extension-api", + "version": "0.1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "argocd-otel-extension-api", + "version": "0.1.1", + "dependencies": { + "express": "^4.21.2", + "http-proxy-middleware": "^3.0.7" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", + "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.7.tgz", + "integrity": "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.18.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..c91692e --- /dev/null +++ b/backend/package.json @@ -0,0 +1,16 @@ +{ + "name": "argocd-otel-extension-api", + "version": "0.1.1", + "description": "Backend API service for ArgoCD OTEL extension - proxies Prometheus and Tempo endpoints", + "main": "src/server.js", + "scripts": { + "start": "node src/server.js" + }, + "dependencies": { + "express": "^4.21.2", + "http-proxy-middleware": "^3.0.7" + }, + "engines": { + "node": ">=20" + } +} diff --git a/backend/src/server.js b/backend/src/server.js new file mode 100644 index 0000000..ff05af0 --- /dev/null +++ b/backend/src/server.js @@ -0,0 +1,72 @@ +'use strict'; + +const express = require('express'); +const { createProxyMiddleware } = require('http-proxy-middleware'); + +const PORT = parseInt(process.env.PORT || '8000', 10); +const PROMETHEUS_BASE_URL = process.env.PROMETHEUS_BASE_URL || ''; +const TEMPO_BASE_URL = process.env.TEMPO_BASE_URL || ''; +const LOG_LEVEL = (process.env.LOG_LEVEL || 'INFO').toUpperCase(); + +const app = express(); + +function log(level, message) { + const levels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }; + if ((levels[level] || 0) >= (levels[LOG_LEVEL] || 1)) { + console.log(JSON.stringify({ time: new Date().toISOString(), level, message })); + } +} + +app.get('/healthz', (_req, res) => { + res.json({ status: 'ok' }); +}); + +if (PROMETHEUS_BASE_URL) { + app.use( + '/prometheus', + createProxyMiddleware({ + target: PROMETHEUS_BASE_URL, + changeOrigin: true, + pathRewrite: { '^/prometheus': '' }, + on: { + error: (err, _req, res) => { + log('ERROR', `Prometheus proxy error: ${err.message}`); + res.status(502).json({ error: 'Bad Gateway', detail: err.message }); + }, + }, + }) + ); + log('INFO', `Prometheus proxy enabled → ${PROMETHEUS_BASE_URL}`); +} else { + app.use('/prometheus', (_req, res) => { + res.status(503).json({ error: 'PROMETHEUS_BASE_URL not configured' }); + }); + log('WARN', 'PROMETHEUS_BASE_URL not set; /prometheus routes will return 503'); +} + +if (TEMPO_BASE_URL) { + app.use( + '/tempo', + createProxyMiddleware({ + target: TEMPO_BASE_URL, + changeOrigin: true, + pathRewrite: { '^/tempo': '' }, + on: { + error: (err, _req, res) => { + log('ERROR', `Tempo proxy error: ${err.message}`); + res.status(502).json({ error: 'Bad Gateway', detail: err.message }); + }, + }, + }) + ); + log('INFO', `Tempo proxy enabled → ${TEMPO_BASE_URL}`); +} else { + app.use('/tempo', (_req, res) => { + res.status(503).json({ error: 'TEMPO_BASE_URL not configured' }); + }); + log('WARN', 'TEMPO_BASE_URL not set; /tempo routes will return 503'); +} + +app.listen(PORT, () => { + log('INFO', `argocd-otel-extension-api listening on port ${PORT}`); +}); From 1e9fea9def1193053df357965048292b04fb3d30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Jun 2026 05:44:23 +0000 Subject: [PATCH 14/33] fix: remove internal error details from proxy error responses; use npm --omit=dev --- backend/Dockerfile | 2 +- backend/src/server.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 75399ff..27739fc 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,7 +1,7 @@ FROM node:20-alpine WORKDIR /app COPY package*.json ./ -RUN npm ci --only=production +RUN npm ci --omit=dev COPY src/ ./src/ EXPOSE 8000 USER node diff --git a/backend/src/server.js b/backend/src/server.js index ff05af0..9c94a6d 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -31,7 +31,7 @@ if (PROMETHEUS_BASE_URL) { on: { error: (err, _req, res) => { log('ERROR', `Prometheus proxy error: ${err.message}`); - res.status(502).json({ error: 'Bad Gateway', detail: err.message }); + res.status(502).json({ error: 'Bad Gateway' }); }, }, }) @@ -54,7 +54,7 @@ if (TEMPO_BASE_URL) { on: { error: (err, _req, res) => { log('ERROR', `Tempo proxy error: ${err.message}`); - res.status(502).json({ error: 'Bad Gateway', detail: err.message }); + res.status(502).json({ error: 'Bad Gateway' }); }, }, }) From a512b7e8b032082baffe926fa1ff0792d6d33bec Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Tue, 23 Jun 2026 05:27:54 +0000 Subject: [PATCH 15/33] feat: update OTEL backend references to use new image and service names --- main.tf | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/main.tf b/main.tf index 991c36b..9322340 100644 --- a/main.tf +++ b/main.tf @@ -72,7 +72,7 @@ variable "otel_extension_version" { variable "otel_backend_tag" { type = string - description = "Image tag (SHA or semver) for ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api" + description = "Image tag (SHA or semver) for ghcr.io/glueops/argocd-extension-backend-api" default = "v0.1.1" } @@ -94,7 +94,7 @@ locals { " - name: otel-extension", " backend:", " services:", - " - url: http://otel-extension-api.glueops-core.svc.cluster.local:8000", + " - url: http://argocd-extension-backend-api.glueops-core.svc.cluster.local:8000", ]) : "" otel_rbac_policies = var.otel_enabled ? join("\n", [ " p, role:readonly, extensions, invoke, otel-extension, allow", @@ -110,27 +110,25 @@ locals { " value: \"https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz\"", " - name: EXTENSION_VERSION", " value: \"placeholder_otel_extension_semver\"", - " - name: EXTENSION_ENABLED", - " value: \"true\"", ]) : "" otel_backend_objects = var.otel_enabled ? join("\n", [ "", " - apiVersion: apps/v1", " kind: Deployment", " metadata:", - " name: otel-extension-api", + " name: argocd-extension-backend-api", " namespace: glueops-core", " labels:", - " app.kubernetes.io/name: otel-extension-api", + " app.kubernetes.io/name: argocd-extension-backend-api", " spec:", " replicas: 2", " selector:", " matchLabels:", - " app.kubernetes.io/name: otel-extension-api", + " app.kubernetes.io/name: argocd-extension-backend-api", " template:", " metadata:", " labels:", - " app.kubernetes.io/name: otel-extension-api", + " app.kubernetes.io/name: argocd-extension-backend-api", " spec:", " nodeSelector:", " glueops.dev/role: glueops-platform", @@ -140,8 +138,8 @@ locals { " value: \"glueops-platform\"", " effect: \"NoSchedule\"", " containers:", - " - name: otel-extension-api", - " image: \"ghcr.repo.gpkg.io/glueops/argocd-otel-extension-api:placeholder_otel_backend_tag\"", + " - name: argocd-extension-backend-api", + " image: \"ghcr.io/glueops/argocd-extension-backend-api:placeholder_otel_backend_tag\"", " imagePullPolicy: IfNotPresent", " ports:", " - name: http", @@ -179,14 +177,14 @@ locals { " - apiVersion: v1", " kind: Service", " metadata:", - " name: otel-extension-api", + " name: argocd-extension-backend-api", " namespace: glueops-core", " labels:", - " app.kubernetes.io/name: otel-extension-api", + " app.kubernetes.io/name: argocd-extension-backend-api", " spec:", " type: ClusterIP", " selector:", - " app.kubernetes.io/name: otel-extension-api", + " app.kubernetes.io/name: argocd-extension-backend-api", " ports:", " - name: http", " port: 8000", From 6875002c74e930fa4dd8f0d8a0ef83b7eff0f3f9 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Tue, 23 Jun 2026 11:21:27 +0000 Subject: [PATCH 16/33] feat: update OTEL extension and backend version to v0.1.2 --- README.md | 4 ++-- main.tf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6386eec..656a75d 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ module "argocd_helm_values" { argocd_app_version = "v2.8.6" gatekeeper_tag = "v1.0.0" otel_enabled = true - otel_extension_version = "v0.1.1" - otel_backend_tag = "v0.1.1" + otel_extension_version = "v0.1.2" + otel_backend_tag = "v0.1.2" tempo_base_url = "http://tempo.glueops-core-tempo.svc.cluster.local:3200" } diff --git a/main.tf b/main.tf index 9322340..9bc1b14 100644 --- a/main.tf +++ b/main.tf @@ -66,8 +66,8 @@ variable "otel_enabled" { variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.1)" - default = "v0.1.1" + description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.2)" + default = "v0.1.2" } variable "otel_backend_tag" { From a24cf8db4b884bea4ac2292d1132d37d6ce41376 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 20 Jul 2026 05:31:01 +0000 Subject: [PATCH 17/33] feat: ship OTEL extension frontend to every cluster Makes the Argo CD OTEL UI extension unconditional and removes the duplicate backend this module was deploying. Always on: drops var.otel_enabled. Safe because the frontend renders nothing when it has no links -- a cluster whose backend is not up shows no panel at all rather than an error box. That behaviour only exists from v0.1.3 onward, so the version default must never go below it. Backend ownership: platform-helm-chart-platform owns the backend and deploys it into glueops-core-argocd-extension-backend. This module was deploying a second Deployment/Service of the same image into glueops-core, so enabling otel would have run two copies. Removed, along with the now-unused otel_backend_tag and tempo_base_url. Backend URL: was glueops-core, which does not resolve (verified NXDOMAIN in-cluster). Both the Service name and its namespace are hardcoded constants in the platform chart, so this URL is identical on every cluster -- there is deliberately nothing per-cluster to substitute. Installer image: the chart defaults it to quay.io directly, unlike every other image on the platform. It runs as an initContainer on argocd-server, so a failed pull takes the Argo CD UI down rather than just disabling the extension. Pinned to the gpkg mirror. Verified by rendering the module output and parsing it: valid YAML, no unreplaced placeholders, tenant RBAC policies preserved (otel policies are appended, not substituted), and no backend objects in extraObjects. Co-Authored-By: Claude Opus 4.8 --- argocd.yaml.tpl | 5 +- main.tf | 202 +++++++++++------------------------------------- 2 files changed, 48 insertions(+), 159 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 8c626b7..946e3a7 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -165,7 +165,9 @@ applicationSet: configs: params: server.insecure: true - server.enable.proxy.extension: placeholder_otel_enabled + # Always true: the OTEL extension ships to every cluster. Required for + # argocd-server to proxy the extension's calls to its backend. + server.enable.proxy.extension: true cm: # @ignored timeout.reconciliation: 10s @@ -300,7 +302,6 @@ extraObjects: name: argocd-server port: number: 80 -# placeholder_otel_backend_objects - apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/main.tf b/main.tf index 9bc1b14..d800dac 100644 --- a/main.tf +++ b/main.tf @@ -58,51 +58,57 @@ variable "gatekeeper_tag" { description = "Image tag (SHA or semver) for ghcr.repo.gpkg.io/glueops/gatekeeper.platform.glueops.dev" } -variable "otel_enabled" { - type = bool - description = "Enable or disable the global ArgoCD OTEL extension and its backend service for this tenant" - default = false -} - +# The OTEL extension frontend is always on, for every cluster -- there is no +# enable/disable switch. That is safe because the frontend renders NOTHING when it +# has no links to show (see StatusPanel in GlueOps/argo-cd-ui-extention): a cluster +# whose backend is not up yet shows no panel at all, rather than an error box. +# This only holds from v0.1.3 onward; v0.1.2 and earlier render a permanent +# "Observability unavailable" box instead, so do NOT pin this below v0.1.3. +# +# Scope: this module configures the FRONTEND only. The backend (Deployment/Service +# argocd-extension-backend-api) is owned by platform-helm-chart-platform, which +# deploys it as an Argo CD Application into glueops-core-argocd-extension-backend. +# This module must never deploy a second copy of it. variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.2)" - default = "v0.1.2" -} - -variable "otel_backend_tag" { - type = string - description = "Image tag (SHA or semver) for ghcr.io/glueops/argocd-extension-backend-api" - default = "v0.1.1" -} - -variable "tempo_base_url" { - type = string - description = "In-cluster Tempo base URL for trace search. Leave empty to disable traces while keeping metrics enabled." - default = "" + description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.3-rc1). Must be >= v0.1.3: earlier releases do not hide the panel when no data is present." + default = "v0.1.3-rc1" } locals { - otel_enabled_string = var.otel_enabled ? "true" : "false" otel_extension_version_trimmed = trimspace(var.otel_extension_version) - otel_backend_tag_trimmed = trimspace(var.otel_backend_tag) - tempo_base_url_trimmed = trimspace(var.tempo_base_url) otel_extension_semver = trimprefix(local.otel_extension_version_trimmed, "v") - otel_extension_config = var.otel_enabled ? join("\n", [ + + # The backend Service DNS is the SAME on every cluster: both the Service name and + # its namespace are hardcoded constants in platform-helm-chart-platform + # (templates/application-argocd-extension-backend.yaml), not derived from + # captain_domain or the cluster environment. So there is deliberately nothing + # per-cluster to substitute here. + # + # The namespace is glueops-core-argocd-extension-backend -- the Application's + # destination namespace -- NOT glueops-core, which does not resolve. + otel_extension_config = join("\n", [ " extension.config: |", " extensions:", " - name: otel-extension", " backend:", " services:", - " - url: http://argocd-extension-backend-api.glueops-core.svc.cluster.local:8000", - ]) : "" - otel_rbac_policies = var.otel_enabled ? join("\n", [ + " - url: http://argocd-extension-backend-api.glueops-core-argocd-extension-backend.svc.cluster.local:8000", + ]) + otel_rbac_policies = join("\n", [ " p, role:readonly, extensions, invoke, otel-extension, allow", " p, role:admin, extensions, invoke, otel-extension, allow", - ]) : "" - otel_server_extensions = var.otel_enabled ? join("\n", [ + ]) + otel_server_extensions = join("\n", [ " extensions:", " enabled: true", + # The chart defaults this installer image to quay.io directly, unlike every + # other image on the platform. Pin it to the gpkg mirror so clusters that + # cannot egress to quay.io (or that would hit its rate limits) still start: + # this runs as an initContainer on argocd-server, so a failed pull takes the + # Argo CD UI down rather than just disabling the extension. + " image:", + " repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer", " extensionList:", " - name: otel-extension", " env:", @@ -110,88 +116,7 @@ locals { " value: \"https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz\"", " - name: EXTENSION_VERSION", " value: \"placeholder_otel_extension_semver\"", - ]) : "" - otel_backend_objects = var.otel_enabled ? join("\n", [ - "", - " - apiVersion: apps/v1", - " kind: Deployment", - " metadata:", - " name: argocd-extension-backend-api", - " namespace: glueops-core", - " labels:", - " app.kubernetes.io/name: argocd-extension-backend-api", - " spec:", - " replicas: 2", - " selector:", - " matchLabels:", - " app.kubernetes.io/name: argocd-extension-backend-api", - " template:", - " metadata:", - " labels:", - " app.kubernetes.io/name: argocd-extension-backend-api", - " spec:", - " nodeSelector:", - " glueops.dev/role: glueops-platform", - " tolerations:", - " - key: \"glueops.dev/role\"", - " operator: \"Equal\"", - " value: \"glueops-platform\"", - " effect: \"NoSchedule\"", - " containers:", - " - name: argocd-extension-backend-api", - " image: \"ghcr.io/glueops/argocd-extension-backend-api:placeholder_otel_backend_tag\"", - " imagePullPolicy: IfNotPresent", - " ports:", - " - name: http", - " containerPort: 8000", - " protocol: TCP", - " env:", - " - name: PORT", - " value: \"8000\"", - " - name: PROMETHEUS_BASE_URL", - " value: \"http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090\"", - " - name: TEMPO_BASE_URL", - " value: \"placeholder_tempo_base_url\"", - " - name: LOG_LEVEL", - " value: \"INFO\"", - " readinessProbe:", - " httpGet:", - " path: /healthz", - " port: http", - " initialDelaySeconds: 5", - " periodSeconds: 10", - " livenessProbe:", - " httpGet:", - " path: /healthz", - " port: http", - " initialDelaySeconds: 15", - " periodSeconds: 20", - " resources:", - " requests:", - " cpu: 50m", - " memory: 64Mi", - " limits:", - " cpu: 250m", - " memory: 256Mi", - "", - " - apiVersion: v1", - " kind: Service", - " metadata:", - " name: argocd-extension-backend-api", - " namespace: glueops-core", - " labels:", - " app.kubernetes.io/name: argocd-extension-backend-api", - " spec:", - " type: ClusterIP", - " selector:", - " app.kubernetes.io/name: argocd-extension-backend-api", - " ports:", - " - name: http", - " port: 8000", - " targetPort: http", - " protocol: TCP", - ]) : "" - + ]) rendered_argocd_values_tenant = replace( data.local_file.argocd_template.content, "placeholder_tenant_key", @@ -234,14 +159,8 @@ locals { var.gatekeeper_tag ) - rendered_argocd_values_otel_enabled = replace( - local.rendered_argocd_values_gatekeeper, - "placeholder_otel_enabled", - local.otel_enabled_string - ) - rendered_argocd_values_otel_extension_config = replace( - local.rendered_argocd_values_otel_enabled, + local.rendered_argocd_values_gatekeeper, " # placeholder_otel_extension_config", local.otel_extension_config ) @@ -258,63 +177,32 @@ locals { local.otel_server_extensions ) - rendered_argocd_values_otel_backend_objects = replace( - local.rendered_argocd_values_otel_server_extensions, - "# placeholder_otel_backend_objects", - local.otel_backend_objects - ) - rendered_argocd_values_otel_version = replace( - local.rendered_argocd_values_otel_backend_objects, + local.rendered_argocd_values_otel_server_extensions, "placeholder_otel_extension_version", local.otel_extension_version_trimmed ) - rendered_argocd_values_otel_semver = replace( + rendered_argocd_values = replace( local.rendered_argocd_values_otel_version, "placeholder_otel_extension_semver", local.otel_extension_semver ) - - rendered_argocd_values_otel_backend_tag = replace( - local.rendered_argocd_values_otel_semver, - "placeholder_otel_backend_tag", - local.otel_backend_tag_trimmed - ) - - rendered_argocd_values = replace( - local.rendered_argocd_values_otel_backend_tag, - "placeholder_tempo_base_url", - local.tempo_base_url_trimmed - ) } output "helm_values" { value = local.rendered_argocd_values + # The extension is always on, so these are unconditional: an empty or malformed + # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. precondition { - condition = !var.otel_enabled || trimspace(var.otel_extension_version) != "" - error_message = "otel_extension_version must be non-empty when otel_enabled is true" - } - - precondition { - condition = !var.otel_enabled || length(regexall("\\s", local.otel_extension_version_trimmed)) == 0 - error_message = "otel_extension_version must not contain whitespace when otel_enabled is true" - } - - precondition { - condition = !var.otel_enabled || trimspace(var.otel_backend_tag) != "" - error_message = "otel_backend_tag must be non-empty when otel_enabled is true" - } - - precondition { - condition = !var.otel_enabled || length(regexall("\\s", local.otel_backend_tag_trimmed)) == 0 - error_message = "otel_backend_tag must not contain whitespace when otel_enabled is true" + condition = local.otel_extension_version_trimmed != "" + error_message = "otel_extension_version must be non-empty" } precondition { - condition = !var.otel_enabled || local.tempo_base_url_trimmed == "" || length(regexall("\\s", local.tempo_base_url_trimmed)) == 0 - error_message = "tempo_base_url must not contain whitespace when otel_enabled is true" + condition = length(regexall("\\s", local.otel_extension_version_trimmed)) == 0 + error_message = "otel_extension_version must not contain whitespace" } } From 3a03379c349ce29ef86edfd256aa27ad1e6a92a0 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya Date: Mon, 20 Jul 2026 05:32:15 +0000 Subject: [PATCH 18/33] docs: clarify why the otel version default is a prerelease The previous comment said 'do NOT pin below v0.1.3' while the default was v0.1.3-rc1, which sorts below v0.1.3 -- the rule contradicted the value it was guarding. State the actual constraint (a release containing PR #25's hide-when-empty behaviour) and why a prerelease is pinned for now. Co-Authored-By: Claude Opus 4.8 --- main.tf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/main.tf b/main.tf index d800dac..e936e60 100644 --- a/main.tf +++ b/main.tf @@ -62,8 +62,14 @@ variable "gatekeeper_tag" { # enable/disable switch. That is safe because the frontend renders NOTHING when it # has no links to show (see StatusPanel in GlueOps/argo-cd-ui-extention): a cluster # whose backend is not up yet shows no panel at all, rather than an error box. -# This only holds from v0.1.3 onward; v0.1.2 and earlier render a permanent -# "Observability unavailable" box instead, so do NOT pin this below v0.1.3. +# That behaviour landed in GlueOps/argo-cd-ui-extention PR #25. Every release cut +# before it -- v0.1.2 and earlier, and anything built from main until #25 merges -- +# renders a permanent "Observability unavailable" box instead. Shipping one of those +# always-on would paint that box on every application in every cluster. +# +# The default below is deliberately a PRERELEASE: v0.1.3-rc1 is built from that PR's +# branch and is currently the only published tag with the hide-when-empty behaviour. +# Once #25 merges, cut a real v0.1.3 from main and bump this default to it. # # Scope: this module configures the FRONTEND only. The backend (Deployment/Service # argocd-extension-backend-api) is owned by platform-helm-chart-platform, which @@ -71,7 +77,7 @@ variable "gatekeeper_tag" { # This module must never deploy a second copy of it. variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball (example: v0.1.3-rc1). Must be >= v0.1.3: earlier releases do not hide the panel when no data is present." + description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be a release that hides the panel when there is no data (v0.1.3-rc1, or v0.1.3+ once cut); v0.1.2 and earlier render a permanent error box." default = "v0.1.3-rc1" } From 177fdfd23d8fbd16e700f7c41d4a5ef6c5d6a255 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Tue, 8 Sep 2026 07:27:20 +0000 Subject: [PATCH 19/33] feat: ship OTEL extension v0.1.3-rc2 (no status panel reload flash) rc1 blanked the panel behind "Loading links..." on every Argo CD reconcile: Argo CD remounts status-panel extensions when the Application object updates, and with timeout.reconciliation: 10s that is roughly every 10 seconds. Each remount reset the panel and refetched /api/links, once per reconcile per open application. rc2 caches the links per application and seeds state from that cache, so a remount renders immediately with no flash and no refetch. It keeps rc1's render-nothing-when-empty behaviour, so it is still safe to ship to every cluster. Co-Authored-By: Claude Opus 5 --- main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.tf b/main.tf index e936e60..b210d31 100644 --- a/main.tf +++ b/main.tf @@ -77,8 +77,8 @@ variable "gatekeeper_tag" { # This module must never deploy a second copy of it. variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be a release that hides the panel when there is no data (v0.1.3-rc1, or v0.1.3+ once cut); v0.1.2 and earlier render a permanent error box." - default = "v0.1.3-rc1" + description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be a release that hides the panel when there is no data (v0.1.3-rc1 or newer); v0.1.2 and earlier render a permanent error box. v0.1.3-rc2 additionally stops the panel blanking its links on every Argo CD reconcile." + default = "v0.1.3-rc2" } locals { From 588eb2d403a0ca24f9cb8d6b73ccb3a06fb3dea3 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Tue, 8 Sep 2026 10:59:54 +0000 Subject: [PATCH 20/33] chore: drop the duplicate backend publisher and fix the stale OTEL docs Prepares this branch for merge. As it stood, merging would have started publishing a second, unused backend image on every release, forever. .github/workflows/release.yml triggered on `release: [published]` and pushed ghcr.io/glueops/argocd-otel-extension-api, built from backend/ in this repo. That image is a duplicate: the real backend is argocd-extension-backend-api, built from GlueOps/argo-cd-extention-backend and deployed by the platform chart as the glueops-argocd-extension-backend Application. main.tf here says as much -- "This module must never deploy a second copy of it." The duplicate is referenced nowhere in the delivery path, and clusters run argocd-extension-backend-api. Once release-please starts cutting releases from main, that workflow would fire on each one. Removes the workflow and backend/. Leaves .github/release.yml alone -- a different file, org-managed via internal-GlueOps/github-shared-files-sync and present on main too. README documented an interface that no longer exists: otel_enabled ("The default is false, so tenants must opt in explicitly" -- the opposite of current behaviour), otel_backend_tag, tempo_base_url, and a placeholder_otel_enabled that is not in the template. The example also omitted argocd_rbac_policies and carried a credential-shaped client_secret. Rewritten against the eight variables main.tf actually declares. No rendering logic changed: main.tf and argocd.yaml.tpl are untouched and the module output is byte-identical (13256 bytes, sha 57f5c2ab). Co-Authored-By: Claude Opus 5 --- .github/workflows/release.yml | 42 -- README.md | 35 +- backend/.dockerignore | 3 - backend/Dockerfile | 8 - backend/README.md | 46 -- backend/package-lock.json | 1041 --------------------------------- backend/package.json | 16 - backend/src/server.js | 72 --- 8 files changed, 17 insertions(+), 1246 deletions(-) delete mode 100644 .github/workflows/release.yml delete mode 100644 backend/.dockerignore delete mode 100644 backend/Dockerfile delete mode 100644 backend/README.md delete mode 100644 backend/package-lock.json delete mode 100644 backend/package.json delete mode 100644 backend/src/server.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 929454a..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Release - -on: - release: - types: [published] - -permissions: - contents: read - packages: write - -jobs: - build-and-push-backend: - name: Build and push backend image - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/glueops/argocd-otel-extension-api - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: ./backend - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/README.md b/README.md index 656a75d..b086a06 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,10 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_tenant_key` with your tenant/company key. Example: `antoniostacos` - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - - OTEL is tenant-overridable through the Terraform module inputs: - - `otel_enabled` enables or disables the global ArgoCD OTEL extension for the tenant. The default is `false`, so tenants must opt in explicitly. - - `otel_extension_version` sets the GitHub release tag used for the extension tarball. - - `otel_backend_tag` sets the OTEL backend API image tag. - - `tempo_base_url` sets the in-cluster Tempo endpoint. Leave it empty to disable traces while keeping metrics enabled. - - If you are installing from the downloaded `argocd.yaml` directly instead of using Terraform, replace `placeholder_otel_enabled` with `true` or `false` before running Helm. Leave the OTEL placeholder comments in place to keep OTEL disabled, or replace those placeholder comments with concrete OTEL config, RBAC, server extension, and backend object blocks if you want OTEL enabled without Terraform. - - The OTEL extension is defined in `argocd.yaml` and loaded by ArgoCD itself, so it is global for all Argo applications without changing app templates. + - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. + - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.3-rc2`. + - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. + - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. Three of them are *comment lines* whose leading indentation is load-bearing, because the module renders by plain string replacement rather than templating: `placeholder_otel_extension_config`, `placeholder_otel_rbac_policies` and `placeholder_otel_server_extensions`. - Install ArgoCD @@ -45,17 +42,19 @@ kubectl get pods -n glueops-core ```hcl module "argocd_helm_values" { - source = "git::https://github.com/GlueOps/docs-argocd.git" - tenant_key = "antoniostacos" - cluster_environment = "nonprod" - client_secret = "Zsbui/29YEqoGOzuI8snlqGcdaRYPSLocwLXDB5GhZY=" - glueops_root_domain = "onglueops.com" - argocd_app_version = "v2.8.6" - gatekeeper_tag = "v1.0.0" - otel_enabled = true - otel_extension_version = "v0.1.2" - otel_backend_tag = "v0.1.2" - tempo_base_url = "http://tempo.glueops-core-tempo.svc.cluster.local:3200" + source = "git::https://github.com/GlueOps/docs-argocd.git?ref=v0.20.0" + tenant_key = "antoniostacos" + cluster_environment = "nonprod" + # Must match the dex client secret used in platform.yaml, or login will fail. + client_secret = "" + glueops_root_domain = "onglueops.com" + argocd_rbac_policies = " g, glueops-rocks:super_admins, role:admin\n" + argocd_app_version = "v3.2.12" + gatekeeper_tag = "v0.1.1" + + # Optional. Defaults to v0.1.3-rc2. Must be a release that hides the panel when + # there is no data; v0.1.2 and earlier render a permanent error box. + otel_extension_version = "v0.1.3-rc2" } output "argocd_helm_values" { diff --git a/backend/.dockerignore b/backend/.dockerignore deleted file mode 100644 index f4100b6..0000000 --- a/backend/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -npm-debug.log -.npm diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 27739fc..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM node:20-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm ci --omit=dev -COPY src/ ./src/ -EXPOSE 8000 -USER node -CMD ["node", "src/server.js"] diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 058b098..0000000 --- a/backend/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# argocd-otel-extension-api - -Backend service for the ArgoCD OTEL UI extension. It proxies Prometheus and Tempo endpoints so the extension frontend can query metrics and traces without direct cluster network access. - -## Endpoints - -| Path | Proxied to | -|------|------------| -| `GET /healthz` | Local health check — returns `{"status":"ok"}` | -| `ANY /prometheus/*` | `PROMETHEUS_BASE_URL/*` (path prefix stripped) | -| `ANY /tempo/*` | `TEMPO_BASE_URL/*` (path prefix stripped) | - -### Prometheus example -``` -GET /prometheus/api/v1/query?query=up -``` -Proxied to `$PROMETHEUS_BASE_URL/api/v1/query?query=up`. - -### Tempo example -``` -GET /tempo/api/search?tags=service.name%3Dmyapp -``` -Proxied to `$TEMPO_BASE_URL/api/search?tags=service.name%3Dmyapp`. - -## Environment variables - -| Variable | Required | Default | Description | -|----------|----------|---------|-------------| -| `PORT` | No | `8000` | Port the server listens on | -| `PROMETHEUS_BASE_URL` | Yes (for metrics) | `""` | In-cluster Prometheus base URL, e.g. `http://kps-prometheus.glueops-core-kube-prometheus-stack.svc.cluster.local:9090` | -| `TEMPO_BASE_URL` | No | `""` | In-cluster Tempo base URL, e.g. `http://tempo.glueops-core-tempo.svc.cluster.local:3200`. Leave empty to disable trace proxying. | -| `LOG_LEVEL` | No | `INFO` | Log verbosity: `DEBUG`, `INFO`, `WARN`, or `ERROR` | - -## Running locally - -```bash -npm install -PROMETHEUS_BASE_URL=http://localhost:9090 node src/server.js -``` - -## Container image - -The image is published to GHCR on every release: -``` -ghcr.io/glueops/argocd-otel-extension-api: -``` diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index 5254517..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,1041 +0,0 @@ -{ - "name": "argocd-otel-extension-api", - "version": "0.1.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "argocd-otel-extension-api", - "version": "0.1.1", - "dependencies": { - "express": "^4.21.2", - "http-proxy-middleware": "^3.0.7" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@types/http-proxy": { - "version": "1.17.17", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", - "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.15.1", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.7.tgz", - "integrity": "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.15", - "debug": "^4.3.6", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.3", - "is-plain-object": "^5.0.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": "^14.18.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index c91692e..0000000 --- a/backend/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "argocd-otel-extension-api", - "version": "0.1.1", - "description": "Backend API service for ArgoCD OTEL extension - proxies Prometheus and Tempo endpoints", - "main": "src/server.js", - "scripts": { - "start": "node src/server.js" - }, - "dependencies": { - "express": "^4.21.2", - "http-proxy-middleware": "^3.0.7" - }, - "engines": { - "node": ">=20" - } -} diff --git a/backend/src/server.js b/backend/src/server.js deleted file mode 100644 index 9c94a6d..0000000 --- a/backend/src/server.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -const express = require('express'); -const { createProxyMiddleware } = require('http-proxy-middleware'); - -const PORT = parseInt(process.env.PORT || '8000', 10); -const PROMETHEUS_BASE_URL = process.env.PROMETHEUS_BASE_URL || ''; -const TEMPO_BASE_URL = process.env.TEMPO_BASE_URL || ''; -const LOG_LEVEL = (process.env.LOG_LEVEL || 'INFO').toUpperCase(); - -const app = express(); - -function log(level, message) { - const levels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }; - if ((levels[level] || 0) >= (levels[LOG_LEVEL] || 1)) { - console.log(JSON.stringify({ time: new Date().toISOString(), level, message })); - } -} - -app.get('/healthz', (_req, res) => { - res.json({ status: 'ok' }); -}); - -if (PROMETHEUS_BASE_URL) { - app.use( - '/prometheus', - createProxyMiddleware({ - target: PROMETHEUS_BASE_URL, - changeOrigin: true, - pathRewrite: { '^/prometheus': '' }, - on: { - error: (err, _req, res) => { - log('ERROR', `Prometheus proxy error: ${err.message}`); - res.status(502).json({ error: 'Bad Gateway' }); - }, - }, - }) - ); - log('INFO', `Prometheus proxy enabled → ${PROMETHEUS_BASE_URL}`); -} else { - app.use('/prometheus', (_req, res) => { - res.status(503).json({ error: 'PROMETHEUS_BASE_URL not configured' }); - }); - log('WARN', 'PROMETHEUS_BASE_URL not set; /prometheus routes will return 503'); -} - -if (TEMPO_BASE_URL) { - app.use( - '/tempo', - createProxyMiddleware({ - target: TEMPO_BASE_URL, - changeOrigin: true, - pathRewrite: { '^/tempo': '' }, - on: { - error: (err, _req, res) => { - log('ERROR', `Tempo proxy error: ${err.message}`); - res.status(502).json({ error: 'Bad Gateway' }); - }, - }, - }) - ); - log('INFO', `Tempo proxy enabled → ${TEMPO_BASE_URL}`); -} else { - app.use('/tempo', (_req, res) => { - res.status(503).json({ error: 'TEMPO_BASE_URL not configured' }); - }); - log('WARN', 'TEMPO_BASE_URL not set; /tempo routes will return 503'); -} - -app.listen(PORT, () => { - log('INFO', `argocd-otel-extension-api listening on port ${PORT}`); -}); From 384f500a5b0dc54ec4568db78a65945aacf51a91 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 10:00:26 +0000 Subject: [PATCH 21/33] refactor: write the OTEL blocks in argocd.yaml.tpl instead of building them in HCL The extension is unconditionally installed, so the three injected blocks carried no interpolation at all -- they were static YAML assembled as HCL string lists and pushed in with replace(). Writing them directly in the template removes ~56 lines of main.tf and, more importantly, removes the failure mode: the anchors were *comment lines* at 4, 2 and 6 spaces, and because rendering is plain string replacement the indentation of a comment decided whether the injected YAML landed in the right place. argocd.yaml.tpl now parses as valid YAML as downloaded (verified with yq), so the documented "wget it and use it with helm install" path works without substituting anything structural. Only placeholder_otel_extension_version and placeholder_otel_extension_semver remain, and both are plain scalars. Verified byte-for-byte: rendering the module before and after produces identical YAML content (the only added lines are the explanatory comments, which moved from HCL into the template and now ship in the rendered file), and yq reports the two documents semantically identical. Zero unsubstituted placeholders in the output. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 +- argocd.yaml.tpl | 35 ++++++++++++++++++++++++++--- main.tf | 58 +------------------------------------------------ 3 files changed, 34 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index b086a06..013c445 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.3-rc2`. - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. - - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. Three of them are *comment lines* whose leading indentation is load-bearing, because the module renders by plain string replacement rather than templating: `placeholder_otel_extension_config`, `placeholder_otel_rbac_policies` and `placeholder_otel_server_extensions`. + - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` and `placeholder_otel_extension_semver` are substituted, and both are plain strings. - Install ArgoCD diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 11d7b36..ee4c2bd 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -211,7 +211,20 @@ configs: allowedAudiences: - argocd - toolbox - # placeholder_otel_extension_config + # The Argo CD OTEL observability extension is always installed -- there is no + # enable/disable input. The backend Service DNS is the SAME on every cluster: + # both the Service name and its namespace are hardcoded constants in + # platform-helm-chart-platform (templates/application-argocd-extension-backend.yaml), + # not derived from captain_domain or the cluster environment, so there is + # deliberately nothing per-cluster to substitute here. The namespace is + # glueops-core-argocd-extension-backend -- the Application's destination + # namespace -- NOT glueops-core, which does not resolve. + extension.config: | + extensions: + - name: otel-extension + backend: + services: + - url: http://argocd-extension-backend-api.glueops-core-argocd-extension-backend.svc.cluster.local:8000 rbac: # -- A good reference for this is: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/ # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. @@ -219,10 +232,26 @@ configs: # @default -- `''` (See [values.yaml]) policy.csv: | placeholder_argocd_rbac_policies - # placeholder_otel_rbac_policies + p, role:readonly, extensions, invoke, otel-extension, allow + p, role:admin, extensions, invoke, otel-extension, allow # @ignored server: - # placeholder_otel_server_extensions + extensions: + enabled: true + # The chart defaults this installer image to quay.io directly, unlike every + # other image on the platform. Pin it to the gpkg mirror so clusters that + # cannot egress to quay.io (or that would hit its rate limits) still start: + # this runs as an initContainer on argocd-server, so a failed pull takes the + # Argo CD UI down rather than just disabling the extension. + image: + repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer + extensionList: + - name: otel-extension + env: + - name: EXTENSION_URL + value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" + - name: EXTENSION_VERSION + value: "placeholder_otel_extension_semver" # @ignored metrics: enabled: true diff --git a/main.tf b/main.tf index b210d31..0c29a50 100644 --- a/main.tf +++ b/main.tf @@ -85,44 +85,6 @@ locals { otel_extension_version_trimmed = trimspace(var.otel_extension_version) otel_extension_semver = trimprefix(local.otel_extension_version_trimmed, "v") - # The backend Service DNS is the SAME on every cluster: both the Service name and - # its namespace are hardcoded constants in platform-helm-chart-platform - # (templates/application-argocd-extension-backend.yaml), not derived from - # captain_domain or the cluster environment. So there is deliberately nothing - # per-cluster to substitute here. - # - # The namespace is glueops-core-argocd-extension-backend -- the Application's - # destination namespace -- NOT glueops-core, which does not resolve. - otel_extension_config = join("\n", [ - " extension.config: |", - " extensions:", - " - name: otel-extension", - " backend:", - " services:", - " - url: http://argocd-extension-backend-api.glueops-core-argocd-extension-backend.svc.cluster.local:8000", - ]) - otel_rbac_policies = join("\n", [ - " p, role:readonly, extensions, invoke, otel-extension, allow", - " p, role:admin, extensions, invoke, otel-extension, allow", - ]) - otel_server_extensions = join("\n", [ - " extensions:", - " enabled: true", - # The chart defaults this installer image to quay.io directly, unlike every - # other image on the platform. Pin it to the gpkg mirror so clusters that - # cannot egress to quay.io (or that would hit its rate limits) still start: - # this runs as an initContainer on argocd-server, so a failed pull takes the - # Argo CD UI down rather than just disabling the extension. - " image:", - " repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer", - " extensionList:", - " - name: otel-extension", - " env:", - " - name: EXTENSION_URL", - " value: \"https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz\"", - " - name: EXTENSION_VERSION", - " value: \"placeholder_otel_extension_semver\"", - ]) rendered_argocd_values_tenant = replace( data.local_file.argocd_template.content, "placeholder_tenant_key", @@ -165,26 +127,8 @@ locals { var.gatekeeper_tag ) - rendered_argocd_values_otel_extension_config = replace( - local.rendered_argocd_values_gatekeeper, - " # placeholder_otel_extension_config", - local.otel_extension_config - ) - - rendered_argocd_values_otel_rbac = replace( - local.rendered_argocd_values_otel_extension_config, - " # placeholder_otel_rbac_policies", - local.otel_rbac_policies - ) - - rendered_argocd_values_otel_server_extensions = replace( - local.rendered_argocd_values_otel_rbac, - " # placeholder_otel_server_extensions", - local.otel_server_extensions - ) - rendered_argocd_values_otel_version = replace( - local.rendered_argocd_values_otel_server_extensions, + local.rendered_argocd_values_gatekeeper, "placeholder_otel_extension_version", local.otel_extension_version_trimmed ) From c8ded113106f63f947f8bd19e492097315a0c312 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 10:09:46 +0000 Subject: [PATCH 22/33] refactor: keep main.tf's existing render style instead of rewriting it The locals chain this PR introduced was justified when OTEL needed five substitutions on top of the seven already there -- twelve levels of nested replace() is unreadable. Moving the static blocks into argocd.yaml.tpl cut that to two, so the nest is nine instead of seven and the rewrite no longer pays for itself. It also meant this PR touched how every unrelated placeholder renders, which is review surface the change does not need. main.tf now keeps the nested-replace shape already on main and appends two calls for the extension version and semver. The two version checks move from output preconditions to variable validation blocks. That is where they belong -- they validate an input, not an output -- and it leaves the output block the same shape as main's. Verified they still fire: passing " " fails with "otel_extension_version must be non-empty". Rendered output is unchanged from before this PR's cleanup: YAML content byte-identical with comments stripped, and yq reports the documents semantically identical. Zero unsubstituted placeholders. Co-Authored-By: Claude Opus 5 (1M context) --- main.tf | 94 +++++++++++++++++---------------------------------------- 1 file changed, 27 insertions(+), 67 deletions(-) diff --git a/main.tf b/main.tf index 0c29a50..74d2e6b 100644 --- a/main.tf +++ b/main.tf @@ -79,80 +79,40 @@ variable "otel_extension_version" { type = string description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be a release that hides the panel when there is no data (v0.1.3-rc1 or newer); v0.1.2 and earlier render a permanent error box. v0.1.3-rc2 additionally stops the panel blanking its links on every Argo CD reconcile." default = "v0.1.3-rc2" + + # The extension is always on, so these are unconditional: an empty or malformed + # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. + validation { + condition = trimspace(var.otel_extension_version) != "" + error_message = "otel_extension_version must be non-empty" + } + + validation { + condition = length(regexall("\\s", trimspace(var.otel_extension_version))) == 0 + error_message = "otel_extension_version must not contain whitespace" + } } locals { otel_extension_version_trimmed = trimspace(var.otel_extension_version) otel_extension_semver = trimprefix(local.otel_extension_version_trimmed, "v") - - rendered_argocd_values_tenant = replace( - data.local_file.argocd_template.content, - "placeholder_tenant_key", - var.tenant_key - ) - - rendered_argocd_values_environment = replace( - local.rendered_argocd_values_tenant, - "placeholder_cluster_environment", - var.cluster_environment - ) - - rendered_argocd_values_secret = replace( - local.rendered_argocd_values_environment, - "placeholder_argocd_oidc_client_secret_from_dex", - var.client_secret - ) - - rendered_argocd_values_domain = replace( - local.rendered_argocd_values_secret, - "placeholder_glueops_root_domain", - var.glueops_root_domain - ) - - rendered_argocd_values_rbac = replace( - local.rendered_argocd_values_domain, - " placeholder_argocd_rbac_policies", - var.argocd_rbac_policies - ) - - rendered_argocd_values_app_version = replace( - local.rendered_argocd_values_rbac, - "placeholder_argocd_app_version", - var.argocd_app_version - ) - - rendered_argocd_values_gatekeeper = replace( - local.rendered_argocd_values_app_version, - "placeholder_gatekeeper_tag", - var.gatekeeper_tag - ) - - rendered_argocd_values_otel_version = replace( - local.rendered_argocd_values_gatekeeper, - "placeholder_otel_extension_version", - local.otel_extension_version_trimmed - ) - - rendered_argocd_values = replace( - local.rendered_argocd_values_otel_version, - "placeholder_otel_extension_semver", - local.otel_extension_semver - ) } output "helm_values" { - value = local.rendered_argocd_values - - # The extension is always on, so these are unconditional: an empty or malformed - # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. - precondition { - condition = local.otel_extension_version_trimmed != "" - error_message = "otel_extension_version must be non-empty" - } - - precondition { - condition = length(regexall("\\s", local.otel_extension_version_trimmed)) == 0 - error_message = "otel_extension_version must not contain whitespace" - } + value = replace(replace(replace(replace(replace(replace( + replace( + replace( + replace( + data.local_file.argocd_template.content, + "placeholder_tenant_key", var.tenant_key), + "placeholder_cluster_environment", var.cluster_environment), + "placeholder_argocd_oidc_client_secret_from_dex", var.client_secret), + "placeholder_glueops_root_domain", var.glueops_root_domain), + " placeholder_argocd_rbac_policies", var.argocd_rbac_policies), + "placeholder_argocd_app_version", var.argocd_app_version), + "placeholder_gatekeeper_tag", var.gatekeeper_tag), + "placeholder_otel_extension_version", local.otel_extension_version_trimmed), + "placeholder_otel_extension_semver", local.otel_extension_semver + ) } From b7990e94ea2c3c450b14fa5b5eac9ddfbe624bb2 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 10:10:45 +0000 Subject: [PATCH 23/33] chore: drop diff noise from main.tf tofu fmt had normalised the provider block's alignment, and the comment above otel_extension_version still described v0.1.3-rc1 as unreleased and told the reader to bump the default once PR #25 merged -- both done. Neither line is what this PR is about. Co-Authored-By: Claude Opus 5 (1M context) --- main.tf | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/main.tf b/main.tf index 74d2e6b..785e560 100644 --- a/main.tf +++ b/main.tf @@ -3,10 +3,10 @@ terraform { required_providers { http = { - source = "hashicorp/http" + source = "hashicorp/http" } local = { - source = "hashicorp/local" + source = "hashicorp/local" } } } @@ -62,14 +62,9 @@ variable "gatekeeper_tag" { # enable/disable switch. That is safe because the frontend renders NOTHING when it # has no links to show (see StatusPanel in GlueOps/argo-cd-ui-extention): a cluster # whose backend is not up yet shows no panel at all, rather than an error box. -# That behaviour landed in GlueOps/argo-cd-ui-extention PR #25. Every release cut -# before it -- v0.1.2 and earlier, and anything built from main until #25 merges -- -# renders a permanent "Observability unavailable" box instead. Shipping one of those -# always-on would paint that box on every application in every cluster. -# -# The default below is deliberately a PRERELEASE: v0.1.3-rc1 is built from that PR's -# branch and is currently the only published tag with the hide-when-empty behaviour. -# Once #25 merges, cut a real v0.1.3 from main and bump this default to it. +# Releases before v0.1.3-rc1 render a permanent "Observability unavailable" box +# instead, so shipping one of those always-on would paint that box on every +# application in every cluster. # # Scope: this module configures the FRONTEND only. The backend (Deployment/Service # argocd-extension-backend-api) is owned by platform-helm-chart-platform, which From a10b7617ec20d2786bcdaf0c472ec406b7406145 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 10:15:37 +0000 Subject: [PATCH 24/33] fix: wildcard the extension RBAC instead of naming otel-extension Argo CD denies extension invocation unless a policy allows it, and the policy named otel-extension specifically. Adding a second extension would have meant an RBAC edit reaching every cluster -- exactly the per-cluster churn we are removing elsewhere. The installed set is controlled by server.extensionList in this same file, so the wildcard does not widen what can be invoked, only what can be invoked without another edit. The explanation sits above policy.csv as a YAML comment rather than inside the block scalar: everything under policy.csv is Casbin policy text, and while Casbin does skip "#" lines, RBAC is not worth resting on that. Verified with yq that the rendered policy value contains only policy lines. Co-Authored-By: Claude Opus 5 (1M context) --- argocd.yaml.tpl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index ee4c2bd..429b79d 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -230,10 +230,17 @@ configs: # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. # `development` is the project that all developers are expected to deploy under # @default -- `''` (See [values.yaml]) + # The two extension lines below are wildcarded rather than naming + # otel-extension: Argo CD denies extension invocation unless a policy allows + # it, and the set of installed extensions is controlled by + # server.extensionList in this same file. Naming each one would mean editing + # RBAC on every cluster each time an extension is added, which is the + # per-cluster churn we are trying to remove. Keep them OUTSIDE any comment -- + # everything under policy.csv is Casbin policy text, not YAML. policy.csv: | placeholder_argocd_rbac_policies - p, role:readonly, extensions, invoke, otel-extension, allow - p, role:admin, extensions, invoke, otel-extension, allow + p, role:readonly, extensions, invoke, *, allow + p, role:admin, extensions, invoke, *, allow # @ignored server: extensions: From 6a8a1fbc1428ea32a096aac949f610ab58718428 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 11:06:16 +0000 Subject: [PATCH 25/33] feat: default the OTEL extension to v0.1.3 v0.1.3 is the first non-prerelease of the panel and the first build that renders the links it fetches. Every earlier build gated each category on status === 'ok', and deployment-config is effectively the only category the backend ever marks ok, so the panel showed a single Config Repo button and hid working Loki, Tempo, Prometheus and dashboard links. Verified end to end: the release tarball downloads, contains resources/otel-extension/extensions.js (7725 bytes), and the shipped bundle carries the fix and no longer carries the removed gate. Rendering this module produces the matching EXTENSION_URL, with zero unsubstituted placeholders. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 9 +++++---- main.tf | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 013c445..a83406b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. - - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.3-rc2`. + - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.3`. - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` and `placeholder_otel_extension_semver` are substituted, and both are plain strings. @@ -52,9 +52,10 @@ module "argocd_helm_values" { argocd_app_version = "v3.2.12" gatekeeper_tag = "v0.1.1" - # Optional. Defaults to v0.1.3-rc2. Must be a release that hides the panel when - # there is no data; v0.1.2 and earlier render a permanent error box. - otel_extension_version = "v0.1.3-rc2" + # Optional. Defaults to v0.1.3. Must be v0.1.3 or newer: earlier builds either + # render a permanent error box (v0.1.2 and below) or hide every link category + # the backend marks degraded, leaving only the Config Repo button. + otel_extension_version = "v0.1.3" } output "argocd_helm_values" { diff --git a/main.tf b/main.tf index 785e560..2f64beb 100644 --- a/main.tf +++ b/main.tf @@ -72,8 +72,8 @@ variable "gatekeeper_tag" { # This module must never deploy a second copy of it. variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be a release that hides the panel when there is no data (v0.1.3-rc1 or newer); v0.1.2 and earlier render a permanent error box. v0.1.3-rc2 additionally stops the panel blanking its links on every Argo CD reconcile." - default = "v0.1.3-rc2" + description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be v0.1.3 or newer: v0.1.2 and earlier render a permanent \"Observability unavailable\" box on every application, and every build before v0.1.3 hid any link category the backend marked degraded -- which is all of them except Config Repo." + default = "v0.1.3" # The extension is always on, so these are unconditional: an empty or malformed # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. From 49e5340b5fec3091cab761529efe599c7bdedb72 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Wed, 9 Sep 2026 11:38:35 +0000 Subject: [PATCH 26/33] feat: default the OTEL extension to v0.1.4 Security and correctness hardening, no visible change to the panel: safeHref() gates every rendered href (javascript:/data: XSS, and //host, /\host and tab/newline open-redirect smuggling), requestTimeoutMs rejects NaN and fractional values that make setTimeout fire immediately, categories filter to renderable links before deciding whether to render, React keys cannot collide, and the theme MutationObserver no longer watches document.body's subtree. Verified: the release URL returns HTTP 200, the shipped bundle carries the hardening markers (8058 bytes, up from 7725 in v0.1.3) and still carries the links cache from #69, and rendering this module produces the matching EXTENSION_URL with zero unsubstituted placeholders. Unlike v0.1.3, this tag is an ancestor of main. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 6 +++--- main.tf | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a83406b..a8035e7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. - - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.3`. + - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.4`. - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` and `placeholder_otel_extension_semver` are substituted, and both are plain strings. @@ -52,10 +52,10 @@ module "argocd_helm_values" { argocd_app_version = "v3.2.12" gatekeeper_tag = "v0.1.1" - # Optional. Defaults to v0.1.3. Must be v0.1.3 or newer: earlier builds either + # Optional. Defaults to v0.1.4. Must be v0.1.3 or newer: earlier builds either # render a permanent error box (v0.1.2 and below) or hide every link category # the backend marks degraded, leaving only the Config Repo button. - otel_extension_version = "v0.1.3" + otel_extension_version = "v0.1.4" } output "argocd_helm_values" { diff --git a/main.tf b/main.tf index 2f64beb..6f22bfe 100644 --- a/main.tf +++ b/main.tf @@ -73,7 +73,7 @@ variable "gatekeeper_tag" { variable "otel_extension_version" { type = string description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be v0.1.3 or newer: v0.1.2 and earlier render a permanent \"Observability unavailable\" box on every application, and every build before v0.1.3 hid any link category the backend marked degraded -- which is all of them except Config Repo." - default = "v0.1.3" + default = "v0.1.4" # The extension is always on, so these are unconditional: an empty or malformed # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. From 29d14bde91f81cfff1467af4b757eab5d26a57b6 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 03:38:50 +0000 Subject: [PATCH 27/33] feat: default the OTEL extension to v0.1.5 The status panel no longer appears on clusters without the extension backend. It previously always rendered its bordered panel with the GlueOps logo, and usually without even an explanatory line, because a transport failure resolved as an empty category list and took the success path. v0.1.5 returns null unless there are links to show, and stops calling /api/links once a failure has marked the backend absent. This is what makes the always-on rollout safe: the extension can ship fleet-wide ahead of, or without, its backend and simply not appear. Behaviour change: an application whose categories are all empty now shows no panel at all, where earlier releases showed a logo box. Verified: the release URL returns HTTP 200 and the shipped bundle no longer contains "Loading links", "Observability unavailable" or "Application context not available" -- all three present in v0.1.4 -- while retaining the links cache and the safeHref/timeout hardening. Rendering this module produces the matching EXTENSION_URL with zero unsubstituted placeholders. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 6 +++--- main.tf | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a8035e7..d49d4f2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - Replace `placeholder_cluster_environment` with your cluster_environment name. Example: `nonprod` - The `placeholder_argocd_oidc_client_secret_from_dex` that you specify needs to be the same one you use in the `platform.yaml` for ArgoCD. If they do not match you will not be able to login. - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. - - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.4`. + - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.5`. - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` and `placeholder_otel_extension_semver` are substituted, and both are plain strings. @@ -52,10 +52,10 @@ module "argocd_helm_values" { argocd_app_version = "v3.2.12" gatekeeper_tag = "v0.1.1" - # Optional. Defaults to v0.1.4. Must be v0.1.3 or newer: earlier builds either + # Optional. Defaults to v0.1.5. Must be v0.1.3 or newer: earlier builds either # render a permanent error box (v0.1.2 and below) or hide every link category # the backend marks degraded, leaving only the Config Repo button. - otel_extension_version = "v0.1.4" + otel_extension_version = "v0.1.5" } output "argocd_helm_values" { diff --git a/main.tf b/main.tf index 6f22bfe..c77ed85 100644 --- a/main.tf +++ b/main.tf @@ -73,7 +73,7 @@ variable "gatekeeper_tag" { variable "otel_extension_version" { type = string description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be v0.1.3 or newer: v0.1.2 and earlier render a permanent \"Observability unavailable\" box on every application, and every build before v0.1.3 hid any link category the backend marked degraded -- which is all of them except Config Repo." - default = "v0.1.4" + default = "v0.1.5" # The extension is always on, so these are unconditional: an empty or malformed # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. From d88ec0cd91a7bf4538175df7f1d134ea0e0e3ae8 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 05:10:09 +0000 Subject: [PATCH 28/33] docs: record what the RBAC and download-failure tests actually showed Answers both review questions on #53 with evidence rather than assertion. The role cannot be wildcarded. Argo CD's Casbin matcher resolves the subject with g(r.sub, p.sub) -- a group lookup -- while resource/action/object go through globMatch, so `*` in the subject position is a literal name matching nobody. Verified with `argocd admin settings rbac can` against v3.2.12: with `p, role:*, ...` both role:readonly and role:admin answer No; with a bare `p, *, ...` a real user also answers No; with the current two lines both answer Yes, as does a user mapped in via `g,`. The lines stay as they are. A bad EXTENSION_URL does NOT break the deployment, which is the opposite of what the comment here claimed. Tested against argocd-extension-installer:v0.0.9 in a throwaway pod: a nonexistent domain (curl exit 6) and a 404 from a bad tag (curl exit 22) both leave the initContainer exiting 0, the pod Succeeded, and the main container starting with an empty /tmp/extensions. The installer's EXIT trap runs `rm -rf` on its temp dir and then reads $?, which reports the rm rather than the curl, masking the real code. The old comment conflated two failure modes. An IMAGE PULL failure genuinely is fatal -- the initContainer never starts, so argocd-server never starts -- which is what the gpkg mirror pin protects against. A DOWNLOAD failure is not, and nothing alerts on it: the extension is just silently absent. Rendered output is unchanged apart from these comments; policy.csv still contains only policy lines and there are zero unsubstituted placeholders. Co-Authored-By: Claude Opus 5 (1M context) --- argocd.yaml.tpl | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 429b79d..8b23a74 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -237,6 +237,14 @@ configs: # RBAC on every cluster each time an extension is added, which is the # per-cluster churn we are trying to remove. Keep them OUTSIDE any comment -- # everything under policy.csv is Casbin policy text, not YAML. + # + # The ROLE cannot be wildcarded -- only the object can. Argo CD's Casbin + # matcher resolves the subject with g(r.sub, p.sub), a group lookup, while + # resource/action/object go through globMatch. So `*` in the subject is a + # literal name that matches nobody. Verified with `argocd admin settings rbac + # can` against v3.2.12: with `p, role:*, ...` both role:readonly and + # role:admin answer No, and with a bare `p, *, ...` so does a real user; with + # the two lines below both answer Yes, as does a user mapped in via `g,`. policy.csv: | placeholder_argocd_rbac_policies p, role:readonly, extensions, invoke, *, allow @@ -247,9 +255,23 @@ server: enabled: true # The chart defaults this installer image to quay.io directly, unlike every # other image on the platform. Pin it to the gpkg mirror so clusters that - # cannot egress to quay.io (or that would hit its rate limits) still start: - # this runs as an initContainer on argocd-server, so a failed pull takes the - # Argo CD UI down rather than just disabling the extension. + # cannot egress to quay.io (or would hit its rate limits) still start. + # + # Two failure modes, and only one is fatal -- both tested against + # argocd-extension-installer:v0.0.9 in a throwaway pod: + # + # IMAGE PULL failure IS fatal. The initContainer never starts, so + # argocd-server never starts, and the Argo CD UI is down with no Argo CD + # available to fix it. That is why the mirror pin above matters. + # + # DOWNLOAD failure is NOT fatal. A nonexistent domain (curl exit 6) and a + # 404 from a bad tag (curl exit 22) both leave the initContainer exiting 0 + # and argocd-server starting normally with an empty /tmp/extensions -- the + # panel simply never appears. The installer's EXIT trap runs `rm -rf` on + # its temp dir and then reads $?, which by then reports the rm rather than + # the curl, so the real exit code is masked. Do not rely on a bad + # EXTENSION_URL being caught here: nothing will alert, the extension will + # just be silently absent. image: repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer extensionList: From f7e660c0ab1ac9fce60b78aaf085663a2bc58253 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 09:46:21 +0000 Subject: [PATCH 29/33] fix: scope extension RBAC to otel-extension and the built-in roles Reverts the object wildcard. Adding an extension should be a conscious RBAC decision rather than something a wildcard grants silently, so a second extension now needs a line here. Only Argo CD built-in roles are referenced, deliberately. This template is identical on every cluster while the roles above it come from each tenant's own argocd_rbac_policies -- venus defines a single group->admin mapping, another tenant may define several -- so naming a custom role such as role:developers would leave a dangling reference wherever that tenant does not define it. The consequence is that a user holding neither built-in role cannot invoke the extension, and since v0.1.3 the panel renders nothing rather than an error, so nothing on screen points at RBAC. The agreed fix is `policy.default: role:readonly`, rolled out with the OTel stack. Verified with `argocd admin settings rbac can --default-role role:readonly` on v3.2.12: an arbitrary user goes No -> Yes for the extension and for `get applications`, and stays No for `delete applications`. Worth being explicit that this widens read access to Argo CD generally, not only to this panel -- on venus today a non-super_admin has no access at all. Also recorded that the role itself cannot be wildcarded: Argo CD resolves the subject with g(r.sub, p.sub) while resource/action/object use globMatch, so `p, role:*` and a bare `p, *` both answer No for every subject tested. Rendered output verified: policy.csv contains only policy lines, zero unsubstituted placeholders. Co-Authored-By: Claude Opus 5 (1M context) --- argocd.yaml.tpl | 72 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 8b23a74..2a8c0f7 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -230,25 +230,42 @@ configs: # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. # `development` is the project that all developers are expected to deploy under # @default -- `''` (See [values.yaml]) - # The two extension lines below are wildcarded rather than naming - # otel-extension: Argo CD denies extension invocation unless a policy allows - # it, and the set of installed extensions is controlled by - # server.extensionList in this same file. Naming each one would mean editing - # RBAC on every cluster each time an extension is added, which is the - # per-cluster churn we are trying to remove. Keep them OUTSIDE any comment -- - # everything under policy.csv is Casbin policy text, not YAML. + # Extension invocation is denied unless a policy allows it. These are + # deliberately scoped to the otel-extension object and to role:admin and + # role:readonly -- narrow on purpose, so adding an extension is a conscious + # RBAC decision rather than something a wildcard grants silently. Adding a + # second extension therefore needs a line here. # - # The ROLE cannot be wildcarded -- only the object can. Argo CD's Casbin - # matcher resolves the subject with g(r.sub, p.sub), a group lookup, while - # resource/action/object go through globMatch. So `*` in the subject is a - # literal name that matches nobody. Verified with `argocd admin settings rbac - # can` against v3.2.12: with `p, role:*, ...` both role:readonly and - # role:admin answer No, and with a bare `p, *, ...` so does a real user; with - # the two lines below both answer Yes, as does a user mapped in via `g,`. + # Keep them OUTSIDE any comment: everything under policy.csv is Casbin + # policy text, not YAML. + # + # The ROLE cannot be wildcarded even if you wanted to -- only the object can. + # Argo CD resolves the subject with g(r.sub, p.sub), a group lookup, while + # resource/action/object go through globMatch, so `*` in the subject is a + # literal name matching nobody. Verified with `argocd admin settings rbac + # can` on v3.2.12: `p, role:*, ...` and a bare `p, *, ...` both answer No for + # role:readonly, role:admin and a real user; the two lines below answer Yes, + # as does a user mapped in via `g,`. + # + # Only Argo CD BUILT-IN roles are referenced here on purpose. This template + # is the same on every cluster, while the roles above it come from each + # tenant's own argocd_rbac_policies -- venus defines just one group->admin + # mapping, another tenant may define several -- so a custom role named here + # would be dangling wherever that tenant does not define it. + # + # Consequence: a user holding neither built-in role cannot invoke the + # extension, and since v0.1.3 the panel renders nothing rather than an error, + # so there is no on-screen hint that RBAC is why. The intended fix is + # `policy.default: role:readonly` (not set today), rolled out with the OTel + # stack, which gives every authenticated user the readonly role. Verified + # with `argocd admin settings rbac can --default-role role:readonly`: an + # arbitrary user goes from No to Yes for the extension, still No for + # `delete applications`, and Yes for `get applications` -- so it widens read + # access to Argo CD generally, not just to this panel. policy.csv: | placeholder_argocd_rbac_policies - p, role:readonly, extensions, invoke, *, allow - p, role:admin, extensions, invoke, *, allow + p, role:readonly, extensions, invoke, otel-extension, allow + p, role:admin, extensions, invoke, otel-extension, allow # @ignored server: extensions: @@ -264,14 +281,21 @@ server: # argocd-server never starts, and the Argo CD UI is down with no Argo CD # available to fix it. That is why the mirror pin above matters. # - # DOWNLOAD failure is NOT fatal. A nonexistent domain (curl exit 6) and a - # 404 from a bad tag (curl exit 22) both leave the initContainer exiting 0 - # and argocd-server starting normally with an empty /tmp/extensions -- the - # panel simply never appears. The installer's EXIT trap runs `rm -rf` on - # its temp dir and then reads $?, which by then reports the rm rather than - # the curl, so the real exit code is masked. Do not rely on a bad - # EXTENSION_URL being caught here: nothing will alert, the extension will - # just be silently absent. + # DOWNLOAD failure is NOT fatal, in any of its three forms. A nonexistent + # domain (curl 6), a 404 from a bad tag (curl 22), and an unreachable host + # that hangs until the timeout (curl 28) all leave the initContainer + # exiting 0 and argocd-server starting normally with an empty + # /tmp/extensions -- the panel simply never appears. The installer's EXIT + # trap runs `rm -rf` on its temp dir and then reads $?, which by then + # reports the rm rather than the curl, so the real exit code is masked. Do + # not rely on a bad EXTENSION_URL being caught here: nothing alerts, the + # extension is just silently absent. + # + # Cost of the hanging case: curl's --max-time is 30s, so an unreachable + # host adds exactly 30s to EVERY argocd-server pod start (measured). That + # is bounded and considered acceptable. It is tunable if it ever is not -- + # install.sh reads `download_max_sec="${MAX_DOWNLOAD_SEC:-30}"`, so adding + # MAX_DOWNLOAD_SEC to the env below changes it (verified: 5 -> 5.0s). image: repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer extensionList: From eed3d17f1baa4ef6d07815f5602baeb93a22cc1e Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 10:12:03 +0000 Subject: [PATCH 30/33] chore: drop the .gitignore this PR added It was introduced by this branch solely to ignore backend/node_modules/, and backend/ was removed by the same PR when the duplicate backend publisher was dropped -- the real backend lives in GlueOps/argo-cd-extention-backend and is deployed by the platform chart. That leaves a file whose only line ignores a path that exists on neither this branch nor main. The repo is Terraform plus a template and has nothing to ignore, and main never carried a .gitignore, so removing it restores that. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index baf1908..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -backend/node_modules/ From 8ce77decd1817297d1058694284adad401f45093 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 10:57:43 +0000 Subject: [PATCH 31/33] chore: drop EXTENSION_VERSION, which the installer never reads argocd-extension-installer:v0.0.9 assigns ext_version once at line 100 of install.sh and never references it again -- across all 124 lines that is the only mention. The tarball URL determines everything. Confirmed empirically rather than by reading: installed the real v0.1.5 tarball in a throwaway pod with the variable omitted and got the same file at the same path, md5 56b8f0b9f5e53cad2088624abcd4e763, 8038 bytes -- byte-identical to the released bundle and to what is running on venus -- with exit 0 and no warning. The log shows `ext_version=` empty and no different code path taken. Removing it also removes the second placeholder. main.tf now has one OTEL substitution instead of two, and the otel_extension_semver local and its trimprefix are gone. The two placeholders only ever differed by the leading "v": the URL needs the literal GitHub tag (v0.1.5), EXTENSION_VERSION took the bare semver (0.1.5). Rendered output verified: the diff against the previous render is exactly the two deleted lines, nothing else, and zero unsubstituted placeholders remain. The finding is recorded next to the env var, including the caveat that a newer installer could start using it -- the image is pinned in the same block, so whoever bumps it should re-check. Co-Authored-By: Claude Opus 5 (1M context) --- argocd.yaml.tpl | 9 +++++++-- main.tf | 10 ++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 2a8c0f7..63e65eb 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -301,10 +301,15 @@ server: extensionList: - name: otel-extension env: + # EXTENSION_URL is the only input the installer actually uses. It also + # sets EXTENSION_VERSION in upstream's docs, but v0.0.9's install.sh + # assigns ext_version once (line 100) and never reads it again -- the + # tarball URL determines everything. Verified by installing v0.1.5 with + # the variable omitted: same file, same md5 (56b8f0b9...), same 8038 + # bytes, exit 0, no warning. Re-check this if the installer image above + # is ever bumped. - name: EXTENSION_URL value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" - - name: EXTENSION_VERSION - value: "placeholder_otel_extension_semver" # @ignored metrics: enabled: true diff --git a/main.tf b/main.tf index c77ed85..0899fe6 100644 --- a/main.tf +++ b/main.tf @@ -3,10 +3,10 @@ terraform { required_providers { http = { - source = "hashicorp/http" + source = "hashicorp/http" } local = { - source = "hashicorp/local" + source = "hashicorp/local" } } } @@ -90,12 +90,11 @@ variable "otel_extension_version" { locals { otel_extension_version_trimmed = trimspace(var.otel_extension_version) - otel_extension_semver = trimprefix(local.otel_extension_version_trimmed, "v") } output "helm_values" { - value = replace(replace(replace(replace(replace(replace( + value = replace(replace(replace(replace(replace( replace( replace( replace( @@ -107,7 +106,6 @@ output "helm_values" { " placeholder_argocd_rbac_policies", var.argocd_rbac_policies), "placeholder_argocd_app_version", var.argocd_app_version), "placeholder_gatekeeper_tag", var.gatekeeper_tag), - "placeholder_otel_extension_version", local.otel_extension_version_trimmed), - "placeholder_otel_extension_semver", local.otel_extension_semver + "placeholder_otel_extension_version", local.otel_extension_version_trimmed ) } From d1a312ca3ee84dfd8bd295c1de48320fe20c9532 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Thu, 10 Sep 2026 11:24:20 +0000 Subject: [PATCH 32/33] docs: correct two stale claims in the README The manual-install note still told readers that both placeholder_otel_extension_version and placeholder_otel_extension_semver are substituted. The semver placeholder was removed with EXTENSION_VERSION, so the template now carries eight placeholders and only one of them is OTEL's. The version floor said "v0.1.3 or newer", which understates it now that the extension is installed on every cluster unconditionally. v0.1.3 and v0.1.4 draw an empty bordered panel on a cluster with no backend; only v0.1.5 renders nothing. Since always-on is the design, v0.1.5 is the real floor, and the note now says so and explains what each earlier build does instead. Verified: all eight placeholders in argocd.yaml.tpl are substituted exactly once by main.tf, none orphaned, and the rendered output is unchanged by this commit. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d49d4f2..2049705 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ wget -O argocd.yaml https://raw.githubusercontent.com/GlueOps/docs-argocd/main/a - The OTEL observability extension is **always installed** — there is no enable/disable input. It is defined in `argocd.yaml` and loaded by ArgoCD itself, so it applies to every Argo application without changing app templates. - `otel_extension_version` pins the GitHub release tag of the extension bundle from [GlueOps/argo-cd-ui-extention](https://github.com/GlueOps/argo-cd-ui-extention). Optional; defaults to `v0.1.5`. - The extension's **backend API is not deployed by this module**. It ships with the GlueOps platform chart as the `glueops-argocd-extension-backend` Application; this module only points `extension.config` at its in-cluster Service. - - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` and `placeholder_otel_extension_semver` are substituted, and both are plain strings. + - If you are installing from the downloaded template directly instead of using Terraform, you must substitute every `placeholder_*` yourself. They are all ordinary scalar values, so `argocd.yaml.tpl` is valid YAML as downloaded. The OTEL extension config, its RBAC policies and its `server.extensions` block are written literally in the template -- only `placeholder_otel_extension_version` is substituted, and it is a plain string. - Install ArgoCD @@ -52,9 +52,11 @@ module "argocd_helm_values" { argocd_app_version = "v3.2.12" gatekeeper_tag = "v0.1.1" - # Optional. Defaults to v0.1.5. Must be v0.1.3 or newer: earlier builds either - # render a permanent error box (v0.1.2 and below) or hide every link category - # the backend marks degraded, leaving only the Config Repo button. + # Optional. Defaults to v0.1.5, which is also the recommended floor: the + # extension is installed on every cluster, and only v0.1.5+ renders nothing + # when there is no backend. v0.1.3/v0.1.4 draw an empty bordered panel instead, + # and v0.1.2 and below draw a permanent "Observability unavailable" box on + # every application. otel_extension_version = "v0.1.5" } From af047084a492e940ec8cb34c9dfcef7059424508 Mon Sep 17 00:00:00 2001 From: Irfan Paraniya <> Date: Fri, 11 Sep 2026 07:38:19 +0000 Subject: [PATCH 33/33] docs: trim the OTEL comments down to what is not obvious from the code The comment-to-code ratio on the new OTEL blocks was too high. Cut the long explanatory blocks to a line or two each, or dropped them where the key name already says it: - server.enable.proxy.extension and EXTENSION_URL: dropped - extension.config, RBAC policy, installer image pin: reduced - otel_extension_version: dropped the version history, since v0.1.5 is the first release anyone will use Only comment lines and the otel_extension_version description string changed; no behaviour change. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 6 +--- argocd.yaml.tpl | 82 +++++-------------------------------------------- main.tf | 7 +---- 3 files changed, 9 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 2049705..57425e6 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,7 @@ module "argocd_helm_values" { argocd_app_version = "v3.2.12" gatekeeper_tag = "v0.1.1" - # Optional. Defaults to v0.1.5, which is also the recommended floor: the - # extension is installed on every cluster, and only v0.1.5+ renders nothing - # when there is no backend. v0.1.3/v0.1.4 draw an empty bordered panel instead, - # and v0.1.2 and below draw a permanent "Observability unavailable" box on - # every application. + # Optional. Defaults to v0.1.5. otel_extension_version = "v0.1.5" } diff --git a/argocd.yaml.tpl b/argocd.yaml.tpl index 63e65eb..3f05fd9 100644 --- a/argocd.yaml.tpl +++ b/argocd.yaml.tpl @@ -139,8 +139,6 @@ applicationSet: configs: params: server.insecure: true - # Always true: the OTEL extension ships to every cluster. Required for - # argocd-server to proxy the extension's calls to its backend. server.enable.proxy.extension: true cm: # @ignored @@ -211,14 +209,8 @@ configs: allowedAudiences: - argocd - toolbox - # The Argo CD OTEL observability extension is always installed -- there is no - # enable/disable input. The backend Service DNS is the SAME on every cluster: - # both the Service name and its namespace are hardcoded constants in - # platform-helm-chart-platform (templates/application-argocd-extension-backend.yaml), - # not derived from captain_domain or the cluster environment, so there is - # deliberately nothing per-cluster to substitute here. The namespace is - # glueops-core-argocd-extension-backend -- the Application's destination - # namespace -- NOT glueops-core, which does not resolve. + # The backend Service name and namespace are fixed constants in + # platform-helm-chart-platform; nothing here is per-cluster. extension.config: | extensions: - name: otel-extension @@ -230,38 +222,9 @@ configs: # This default policy is for GlueOps orgs/teams only. Please change it to reflect your own orgs/teams. # `development` is the project that all developers are expected to deploy under # @default -- `''` (See [values.yaml]) - # Extension invocation is denied unless a policy allows it. These are - # deliberately scoped to the otel-extension object and to role:admin and - # role:readonly -- narrow on purpose, so adding an extension is a conscious - # RBAC decision rather than something a wildcard grants silently. Adding a - # second extension therefore needs a line here. - # - # Keep them OUTSIDE any comment: everything under policy.csv is Casbin - # policy text, not YAML. - # - # The ROLE cannot be wildcarded even if you wanted to -- only the object can. - # Argo CD resolves the subject with g(r.sub, p.sub), a group lookup, while - # resource/action/object go through globMatch, so `*` in the subject is a - # literal name matching nobody. Verified with `argocd admin settings rbac - # can` on v3.2.12: `p, role:*, ...` and a bare `p, *, ...` both answer No for - # role:readonly, role:admin and a real user; the two lines below answer Yes, - # as does a user mapped in via `g,`. - # - # Only Argo CD BUILT-IN roles are referenced here on purpose. This template - # is the same on every cluster, while the roles above it come from each - # tenant's own argocd_rbac_policies -- venus defines just one group->admin - # mapping, another tenant may define several -- so a custom role named here - # would be dangling wherever that tenant does not define it. - # - # Consequence: a user holding neither built-in role cannot invoke the - # extension, and since v0.1.3 the panel renders nothing rather than an error, - # so there is no on-screen hint that RBAC is why. The intended fix is - # `policy.default: role:readonly` (not set today), rolled out with the OTel - # stack, which gives every authenticated user the readonly role. Verified - # with `argocd admin settings rbac can --default-role role:readonly`: an - # arbitrary user goes from No to Yes for the extension, still No for - # `delete applications`, and Yes for `get applications` -- so it widens read - # access to Argo CD generally, not just to this panel. + # Extensions are denied unless a policy allows them. Only Argo CD built-in + # roles are referenced: custom roles come from each tenant's own + # argocd_rbac_policies, so naming one here would dangle on other clusters. policy.csv: | placeholder_argocd_rbac_policies p, role:readonly, extensions, invoke, otel-extension, allow @@ -270,44 +233,13 @@ configs: server: extensions: enabled: true - # The chart defaults this installer image to quay.io directly, unlike every - # other image on the platform. Pin it to the gpkg mirror so clusters that - # cannot egress to quay.io (or would hit its rate limits) still start. - # - # Two failure modes, and only one is fatal -- both tested against - # argocd-extension-installer:v0.0.9 in a throwaway pod: - # - # IMAGE PULL failure IS fatal. The initContainer never starts, so - # argocd-server never starts, and the Argo CD UI is down with no Argo CD - # available to fix it. That is why the mirror pin above matters. - # - # DOWNLOAD failure is NOT fatal, in any of its three forms. A nonexistent - # domain (curl 6), a 404 from a bad tag (curl 22), and an unreachable host - # that hangs until the timeout (curl 28) all leave the initContainer - # exiting 0 and argocd-server starting normally with an empty - # /tmp/extensions -- the panel simply never appears. The installer's EXIT - # trap runs `rm -rf` on its temp dir and then reads $?, which by then - # reports the rm rather than the curl, so the real exit code is masked. Do - # not rely on a bad EXTENSION_URL being caught here: nothing alerts, the - # extension is just silently absent. - # - # Cost of the hanging case: curl's --max-time is 30s, so an unreachable - # host adds exactly 30s to EVERY argocd-server pod start (measured). That - # is bounded and considered acceptable. It is tunable if it ever is not -- - # install.sh reads `download_max_sec="${MAX_DOWNLOAD_SEC:-30}"`, so adding - # MAX_DOWNLOAD_SEC to the env below changes it (verified: 5 -> 5.0s). + # Pinned to the gpkg mirror: the chart defaults this installer image to + # quay.io, and a pull failure blocks argocd-server from starting at all. image: repository: quay.repo.gpkg.io/argoprojlabs/argocd-extension-installer extensionList: - name: otel-extension env: - # EXTENSION_URL is the only input the installer actually uses. It also - # sets EXTENSION_VERSION in upstream's docs, but v0.0.9's install.sh - # assigns ext_version once (line 100) and never reads it again -- the - # tarball URL determines everything. Verified by installing v0.1.5 with - # the variable omitted: same file, same md5 (56b8f0b9...), same 8038 - # bytes, exit 0, no warning. Re-check this if the installer image above - # is ever bumped. - name: EXTENSION_URL value: "https://github.com/GlueOps/argo-cd-ui-extention/releases/download/placeholder_otel_extension_version/extension.tar.gz" # @ignored diff --git a/main.tf b/main.tf index 0899fe6..cf3eb73 100644 --- a/main.tf +++ b/main.tf @@ -62,9 +62,6 @@ variable "gatekeeper_tag" { # enable/disable switch. That is safe because the frontend renders NOTHING when it # has no links to show (see StatusPanel in GlueOps/argo-cd-ui-extention): a cluster # whose backend is not up yet shows no panel at all, rather than an error box. -# Releases before v0.1.3-rc1 render a permanent "Observability unavailable" box -# instead, so shipping one of those always-on would paint that box on every -# application in every cluster. # # Scope: this module configures the FRONTEND only. The backend (Deployment/Service # argocd-extension-backend-api) is owned by platform-helm-chart-platform, which @@ -72,11 +69,9 @@ variable "gatekeeper_tag" { # This module must never deploy a second copy of it. variable "otel_extension_version" { type = string - description = "GitHub release tag for the ArgoCD OTEL extension tarball. Must be v0.1.3 or newer: v0.1.2 and earlier render a permanent \"Observability unavailable\" box on every application, and every build before v0.1.3 hid any link category the backend marked degraded -- which is all of them except Config Repo." + description = "GitHub release tag for the ArgoCD OTEL extension tarball." default = "v0.1.5" - # The extension is always on, so these are unconditional: an empty or malformed - # version would render a broken EXTENSION_URL into every cluster's argocd.yaml. validation { condition = trimspace(var.otel_extension_version) != "" error_message = "otel_extension_version must be non-empty"