diff --git a/bundle.yaml b/bundle.yaml
index c71ef211..cb9b96d8 100644
--- a/bundle.yaml
+++ b/bundle.yaml
@@ -10595,7 +10595,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/instance: metrics-reader
app.kubernetes.io/name: clusterrole
@@ -10611,7 +10611,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/instance: proxy-role
app.kubernetes.io/name: clusterrole
@@ -10674,7 +10674,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/instance: proxy-rolebinding
app.kubernetes.io/name: clusterrolebinding
@@ -10693,7 +10693,7 @@ apiVersion: v1
kind: Service
metadata:
labels:
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/instance: controller-manager-metrics-service
app.kubernetes.io/name: service
@@ -10770,7 +10770,8 @@ spec:
containers:
- args:
- --health-probe-bind-address=:8081
- - --metrics-bind-address=127.0.0.1:8082
+ - --metrics-bind-address=:8443
+ - --metrics-secure=true
- --leader-elect
image: docker.io/persesdev/perses-operator:v0.3.2
imagePullPolicy: Always
@@ -10785,6 +10786,9 @@ spec:
- containerPort: 9443
name: webhook-server
protocol: TCP
+ - containerPort: 8443
+ name: https
+ protocol: TCP
readinessProbe:
httpGet:
path: /readyz
@@ -10807,29 +10811,6 @@ spec:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
- - args:
- - --secure-listen-address=0.0.0.0:8443
- - --upstream=http://127.0.0.1:8082/
- - --logtostderr=true
- - --v=0
- image: quay.io/brancz/kube-rbac-proxy:v0.21.0
- name: kube-rbac-proxy
- ports:
- - containerPort: 8443
- name: https
- protocol: TCP
- resources:
- limits:
- cpu: 500m
- memory: 128Mi
- requests:
- cpu: 5m
- memory: 64Mi
- securityContext:
- allowPrivilegeEscalation: false
- capabilities:
- drop:
- - ALL
serviceAccountName: perses-operator-controller-manager
terminationGracePeriodSeconds: 10
volumes:
diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml
index dfa004bc..ae39beea 100644
--- a/config/default/kustomization.yaml
+++ b/config/default/kustomization.yaml
@@ -28,7 +28,7 @@ patches:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
-- path: manager_auth_proxy_patch.yaml
+- path: manager_metrics_patch.yaml
diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_metrics_patch.yaml
similarity index 52%
rename from config/default/manager_auth_proxy_patch.yaml
rename to config/default/manager_metrics_patch.yaml
index 78c682d5..64d537a1 100644
--- a/config/default/manager_auth_proxy_patch.yaml
+++ b/config/default/manager_metrics_patch.yaml
@@ -1,5 +1,6 @@
-# This patch inject a sidecar container which is a HTTP proxy for the
-# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
+# This patch configures the manager to serve the /metrics endpoint
+# with authentication and authorization using controller-runtime's
+# built-in SecureServing and FilterProvider.
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -25,31 +26,13 @@ spec:
values:
- linux
containers:
- - name: kube-rbac-proxy
- securityContext:
- allowPrivilegeEscalation: false
- capabilities:
- drop:
- - "ALL"
- image: quay.io/brancz/kube-rbac-proxy:v0.21.0
+ - name: manager
args:
- - "--secure-listen-address=0.0.0.0:8443"
- - "--upstream=http://127.0.0.1:8082/"
- - "--logtostderr=true"
- - "--v=0"
+ - "--health-probe-bind-address=:8081"
+ - "--metrics-bind-address=:8443"
+ - "--metrics-secure=true"
+ - "--leader-elect"
ports:
- containerPort: 8443
protocol: TCP
name: https
- resources:
- limits:
- cpu: 500m
- memory: 128Mi
- requests:
- cpu: 5m
- memory: 64Mi
- - name: manager
- args:
- - "--health-probe-bind-address=:8081"
- - "--metrics-bind-address=127.0.0.1:8082"
- - "--leader-elect"
diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml
index be2a9bab..335c4a60 100644
--- a/config/rbac/auth_proxy_client_clusterrole.yaml
+++ b/config/rbac/auth_proxy_client_clusterrole.yaml
@@ -4,7 +4,7 @@ metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: metrics-reader
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/part-of: perses-operator
name: metrics-reader
diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml
index 1e8672c2..e8399f77 100644
--- a/config/rbac/auth_proxy_role.yaml
+++ b/config/rbac/auth_proxy_role.yaml
@@ -4,7 +4,7 @@ metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: proxy-role
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/part-of: perses-operator
name: proxy-role
diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml
index b2b3b8ce..49d40ded 100644
--- a/config/rbac/auth_proxy_role_binding.yaml
+++ b/config/rbac/auth_proxy_role_binding.yaml
@@ -4,7 +4,7 @@ metadata:
labels:
app.kubernetes.io/name: clusterrolebinding
app.kubernetes.io/instance: proxy-rolebinding
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/part-of: perses-operator
name: proxy-rolebinding
diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml
index f61b7d17..1a90fc7f 100644
--- a/config/rbac/auth_proxy_service.yaml
+++ b/config/rbac/auth_proxy_service.yaml
@@ -5,7 +5,7 @@ metadata:
control-plane: controller-manager
app.kubernetes.io/name: service
app.kubernetes.io/instance: controller-manager-metrics-service
- app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/component: metrics
app.kubernetes.io/created-by: perses-operator
app.kubernetes.io/part-of: perses-operator
name: controller-manager-metrics-service
diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
index 731832a6..f5c801e0 100644
--- a/config/rbac/kustomization.yaml
+++ b/config/rbac/kustomization.yaml
@@ -10,8 +10,7 @@ resources:
- leader_election_role.yaml
- leader_election_role_binding.yaml
# Comment the following 4 lines if you want to disable
-# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
-# which protects your /metrics endpoint.
+# the authn/authz protection of the /metrics endpoint.
- auth_proxy_service.yaml
- auth_proxy_role.yaml
- auth_proxy_role_binding.yaml
diff --git a/docs/api.md b/docs/api.md
index f5c5ce10..f9ab9fbc 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -135,7 +135,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `config` _[Datasource](#datasource)_ | config specifies the Perses datasource configuration | | Required: \{\}
|
| `client` _[Client](#client)_ | client specifies authentication and TLS configuration for the datasource | | Optional: \{\}
|
-| `instanceSelector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#labelselector-v1-meta)_ | instanceSelector selects Perses instances where this datasource will be created | | Optional: \{\}
|
+| `instanceSelector` _invalid type_ | instanceSelector selects Perses instances where this datasource will be created | | Optional: \{\}
|
#### KubernetesAuth
@@ -209,7 +209,7 @@ Perses is the Schema for the perses API
| --- | --- | --- | --- |
| `apiVersion` _string_ | `perses.dev/v1alpha2` | | |
| `kind` _string_ | `Perses` | | |
-| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
+| `metadata` _invalid type_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
| `spec` _[PersesSpec](#persesspec)_ | spec is the desired state of the Perses resource | | Optional: \{\}
|
| `status` _[PersesStatus](#persesstatus)_ | status is the observed state of the Perses resource | | Optional: \{\}
|
@@ -256,7 +256,7 @@ PersesDashboard is the Schema for the persesdashboards API
| --- | --- | --- | --- |
| `apiVersion` _string_ | `perses.dev/v1alpha2` | | |
| `kind` _string_ | `PersesDashboard` | | |
-| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
+| `metadata` _invalid type_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
| `spec` _[PersesDashboardSpec](#persesdashboardspec)_ | spec is the desired state of the PersesDashboard resource | | Required: \{\}
|
| `status` _[PersesDashboardStatus](#persesdashboardstatus)_ | status is the observed state of the PersesDashboard resource | | Optional: \{\}
|
@@ -275,7 +275,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `config` _[Dashboard](#dashboard)_ | config specifies the Perses dashboard configuration | | Required: \{\}
|
-| `instanceSelector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#labelselector-v1-meta)_ | instanceSelector selects Perses instances where this dashboard will be created | | Optional: \{\}
|
+| `instanceSelector` _invalid type_ | instanceSelector selects Perses instances where this dashboard will be created | | Optional: \{\}
|
#### PersesDashboardStatus
@@ -291,7 +291,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | conditions represent the latest observations of the PersesDashboard resource state | | Optional: \{\}
|
+| `conditions` _invalid type array_ | conditions represent the latest observations of the PersesDashboard resource state | | Optional: \{\}
|
#### PersesDatasource
@@ -308,7 +308,7 @@ PersesDatasource is the Schema for the PersesDatasources API
| --- | --- | --- | --- |
| `apiVersion` _string_ | `perses.dev/v1alpha2` | | |
| `kind` _string_ | `PersesDatasource` | | |
-| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
+| `metadata` _invalid type_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
| `spec` _[DatasourceSpec](#datasourcespec)_ | spec is the desired state of the PersesDatasource resource | | Required: \{\}
|
| `status` _[PersesDatasourceStatus](#persesdatasourcestatus)_ | status is the observed state of the PersesDatasource resource | | Optional: \{\}
|
@@ -326,7 +326,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | conditions represent the latest observations of the PersesDatasource resource state | | Optional: \{\}
|
+| `conditions` _invalid type array_ | conditions represent the latest observations of the PersesDatasource resource state | | Optional: \{\}
|
#### PersesGlobalDatasource
@@ -343,7 +343,7 @@ PersesGlobalDatasource is the Schema for the PersesGlobalDatasources API
| --- | --- | --- | --- |
| `apiVersion` _string_ | `perses.dev/v1alpha2` | | |
| `kind` _string_ | `PersesGlobalDatasource` | | |
-| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
+| `metadata` _invalid type_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
|
| `spec` _[DatasourceSpec](#datasourcespec)_ | spec is the desired state of the PersesGlobalDatasource resource | | Required: \{\}
|
| `status` _[PersesGlobalDatasourceStatus](#persesglobaldatasourcestatus)_ | status is the observed state of the PersesGlobalDatasource resource | | Optional: \{\}
|
@@ -361,7 +361,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | conditions represent the latest observations of the PersesGlobalDatasource resource state | | Optional: \{\}
|
+| `conditions` _invalid type array_ | conditions represent the latest observations of the PersesGlobalDatasource resource state | | Optional: \{\}
|
#### PersesService
@@ -400,23 +400,23 @@ _Appears in:_
| `args` _string array_ | args are extra command-line arguments to pass to the Perses server | | Optional: \{\}
|
| `containerPort` _integer_ | containerPort is the port on which the Perses server listens for HTTP requests | | Maximum: 65535
Minimum: 1
Optional: \{\}
|
| `replicas` _integer_ | replicas is the number of desired pod replicas for the Perses deployment | | Optional: \{\}
|
-| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | resources defines the compute resources configured for the container | | Optional: \{\}
|
+| `resources` _invalid type_ | resources defines the compute resources configured for the container | | Optional: \{\}
|
| `nodeSelector` _object (keys:string, values:string)_ | nodeSelector constrains pods to nodes with matching labels | | Optional: \{\}
|
-| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | tolerations allow pods to schedule onto nodes with matching taints | | Optional: \{\}
|
-| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#affinity-v1-core)_ | affinity specifies the pod's scheduling constraints | | Optional: \{\}
|
+| `tolerations` _invalid type array_ | tolerations allow pods to schedule onto nodes with matching taints | | Optional: \{\}
|
+| `affinity` _invalid type_ | affinity specifies the pod's scheduling constraints | | Optional: \{\}
|
| `image` _string_ | image specifies the container image that should be used for the Perses deployment | | Optional: \{\}
|
| `service` _[PersesService](#persesservice)_ | service specifies the service configuration for the Perses instance | | Optional: \{\}
|
-| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | livenessProbe specifies the liveness probe configuration for the Perses container | | Optional: \{\}
|
-| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | readinessProbe specifies the readiness probe configuration for the Perses container | | Optional: \{\}
|
+| `livenessProbe` _invalid type_ | livenessProbe specifies the liveness probe configuration for the Perses container | | Optional: \{\}
|
+| `readinessProbe` _invalid type_ | readinessProbe specifies the readiness probe configuration for the Perses container | | Optional: \{\}
|
| `tls` _[TLS](#tls)_ | tls specifies the TLS configuration for the Perses instance | | Optional: \{\}
|
| `storage` _[StorageConfiguration](#storageconfiguration)_ | storage configuration used by the StatefulSet | | Optional: \{\}
|
| `serviceAccountName` _string_ | serviceAccountName is the name of the ServiceAccount to use for the Perses deployment or statefulset | | Optional: \{\}
|
-| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podsecuritycontext-v1-core)_ | podSecurityContext holds pod-level security attributes and common container settings
If not specified, defaults to fsGroup: 65534 to ensure proper volume permissions for the nobody user | | Optional: \{\}
|
+| `podSecurityContext` _invalid type_ | podSecurityContext holds pod-level security attributes and common container settings
If not specified, defaults to fsGroup: 65534 to ensure proper volume permissions for the nobody user | | Optional: \{\}
|
| `logLevel` _string_ | logLevel defines the log level for Perses | | Enum: [panic fatal error warning info debug trace]
Optional: \{\}
|
| `logMethodTrace` _boolean_ | logMethodTrace when true, includes the calling method as a field in the log
It can be useful to see immediately where the log comes from | | Optional: \{\}
|
| `provisioning` _[Provisioning](#provisioning)_ | provisioning configuration for provisioning secrets | | Optional: \{\}
|
-| `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volume-v1-core) array_ | volumes allows configuration of additional volumes on the Deployment or StatefulSet definitions.
Volumes specified here will be appended to other operator-managed volumes. | | MaxItems: 20
Optional: \{\}
|
-| `volumeMounts` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volumemount-v1-core) array_ | volumeMounts allows configuration of additional VolumeMounts on the Deployment or StatefulSet definitions.
VolumeMounts specified here will be appended to other operator-managed volume mounts. | | MaxItems: 20
Optional: \{\}
|
+| `volumes` _invalid type array_ | volumes allows configuration of additional volumes on the Deployment or StatefulSet definitions.
Volumes specified here will be appended to other operator-managed volumes. | | MaxItems: 20
Optional: \{\}
|
+| `volumeMounts` _invalid type array_ | volumeMounts allows configuration of additional VolumeMounts on the Deployment or StatefulSet definitions.
VolumeMounts specified here will be appended to other operator-managed volume mounts. | | MaxItems: 20
Optional: \{\}
|
#### PersesStatus
@@ -432,7 +432,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | conditions represent the latest observations of the Perses resource state | | Optional: \{\}
|
+| `conditions` _invalid type array_ | conditions represent the latest observations of the Perses resource state | | Optional: \{\}
|
| `provisioning` _[SecretVersion](#secretversion) array_ | provisioning contains the versions of provisioning secrets currently in use | | Optional: \{\}
|
@@ -463,11 +463,6 @@ _Appears in:_
_Appears in:_
- [Provisioning](#provisioning)
-| Field | Description | Default | Validation |
-| --- | --- | --- | --- |
-| `name` _string_ | Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names | | Optional: \{\}
|
-| `key` _string_ | The key of the secret to select from. Must be a valid secret key. | | |
-| `optional` _boolean_ | Specify whether the Secret or its key must be defined | | Optional: \{\}
|
#### SecretSource
@@ -541,8 +536,8 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `emptyDir` _[EmptyDirVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#emptydirvolumesource-v1-core)_ | emptyDir to use for ephemeral storage.
When set, data will be lost when the pod is deleted or restarted.
Mutually exclusive with PersistentVolumeClaimTemplate. | | Optional: \{\}
|
-| `pvcTemplate` _[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#persistentvolumeclaimspec-v1-core)_ | pvcTemplate is the template for PVCs that will be created.
Mutually exclusive with EmptyDir. | | Optional: \{\}
|
+| `emptyDir` _invalid type_ | emptyDir to use for ephemeral storage.
When set, data will be lost when the pod is deleted or restarted.
Mutually exclusive with PersistentVolumeClaimTemplate. | | Optional: \{\}
|
+| `pvcTemplate` _invalid type_ | pvcTemplate is the template for PVCs that will be created.
Mutually exclusive with EmptyDir. | | Optional: \{\}
|
#### TLS
diff --git a/docs/metrics.md b/docs/metrics.md
index c6fe23cd..c38ee0ff 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -4,15 +4,16 @@ The Perses Operator exposes Prometheus metrics for monitoring operator health an
## Accessing Metrics
-Metrics are exposed on port `8082` at the `/metrics` endpoint:
+Metrics are exposed on port `8443` over HTTPS at the `/metrics` endpoint with authentication and authorization enabled:
```bash
# Port forward to the operator pod
kubectl port-forward -n perses-operator-system \
- deployment/perses-operator-controller-manager 8082:8082
+ deployment/perses-operator-controller-manager 8443:8443
-# View metrics
-curl http://localhost:8082/metrics
+# View metrics (requires a valid bearer token)
+curl -sk https://localhost:8443/metrics \
+ -H "Authorization: Bearer $(kubectl create token -n perses-operator-system perses-operator-controller-manager)"
```
## Available Metrics
diff --git a/go.mod b/go.mod
index c5e3ec9c..6bf2cb09 100644
--- a/go.mod
+++ b/go.mod
@@ -33,8 +33,10 @@ require (
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
+ github.com/blang/semver/v4 v4.0.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/caarlos0/log v0.5.2 // indirect
+ github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.3.2 // indirect
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1 // indirect
@@ -49,6 +51,7 @@ require (
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -56,6 +59,7 @@ require (
github.com/go-git/go-git/v5 v5.16.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.22.4 // indirect
github.com/go-openapi/jsonreference v0.21.4 // indirect
@@ -85,7 +89,9 @@ require (
github.com/goreleaser/fileglob v1.4.0 // indirect
github.com/goreleaser/goreleaser/v2 v2.13.1 // indirect
github.com/goreleaser/nfpm/v2 v2.44.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
@@ -118,6 +124,7 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/spf13/cast v1.7.1 // indirect
+ github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
@@ -129,6 +136,15 @@ require (
github.com/zitadel/oidc/v3 v3.45.4 // indirect
github.com/zitadel/schema v1.3.2 // indirect
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
+ go.opentelemetry.io/otel v1.40.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
+ go.opentelemetry.io/otel/metric v1.40.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.40.0 // indirect
+ go.opentelemetry.io/otel/trace v1.40.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
@@ -147,14 +163,18 @@ require (
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
+ google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.35.1 // indirect
+ k8s.io/apiserver v0.35.1 // indirect
+ k8s.io/component-base v0.35.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
diff --git a/go.sum b/go.sum
index 4e22e942..eb6803da 100644
--- a/go.sum
+++ b/go.sum
@@ -33,6 +33,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/brunoga/deep v1.3.1 h1:bSrL6FhAZa6JlVv4vsi7Hg8SLwroDb1kgDERRVipBCo=
github.com/brunoga/deep v1.3.1/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
@@ -43,6 +45,8 @@ github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
@@ -61,6 +65,7 @@ github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuh
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48=
github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -77,6 +82,8 @@ github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@@ -101,8 +108,11 @@ github.com/go-git/go-git/v5 v5.16.1 h1:TuxMBWNL7R05tXsUGi0kh1vi4tq0WfXNLlIrAkXG1
github.com/go-git/go-git/v5 v5.16.1/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
@@ -149,6 +159,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
@@ -177,8 +189,12 @@ github.com/goreleaser/goreleaser/v2 v2.13.1 h1:/CqB16sE5mQX+HKEHXyPMJZAsK/6n/WRa
github.com/goreleaser/goreleaser/v2 v2.13.1/go.mod h1:FpFenJb/Sa4eWNecTPy82aN02E7Rd4nzKHoZ65RCHSQ=
github.com/goreleaser/nfpm/v2 v2.44.0 h1:TlfLFJX/soK/I9GFbXMtP4SRM74s8sAfdBYNDYjCL8U=
github.com/goreleaser/nfpm/v2 v2.44.0/go.mod h1:sLNhEIplQWuRK5QLxUsMCpkttUiM8lI1cH7rkjmziZU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -273,6 +289,7 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -289,6 +306,9 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -327,6 +347,26 @@ github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI=
github.com/zitadel/schema v1.3.2/go.mod h1:IZmdfF9Wu62Zu6tJJTH3UsArevs3Y4smfJIj3L8fzxw=
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
+go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
+go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
+go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
+go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
+go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
+go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
+go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
+go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
+go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
+go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
+go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
+go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -373,10 +413,14 @@ golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
+google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
+google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -400,14 +444,20 @@ k8s.io/apiextensions-apiserver v0.35.1 h1:p5vvALkknlOcAqARwjS20kJffgzHqwyQRM8vHL
k8s.io/apiextensions-apiserver v0.35.1/go.mod h1:2CN4fe1GZ3HMe4wBr25qXyJnJyZaquy4nNlNmb3R7AQ=
k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
+k8s.io/apiserver v0.35.1 h1:potxdhhTL4i6AYAa2QCwtlhtB1eCdWQFvJV6fXgJzxs=
+k8s.io/apiserver v0.35.1/go.mod h1:BiL6Dd3A2I/0lBnteXfWmCFobHM39vt5+hJQd7Lbpi4=
k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg=
k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c=
+k8s.io/component-base v0.35.1 h1:XgvpRf4srp037QWfGBLFsYMUQJkE5yMa94UsJU7pmcE=
+k8s.io/component-base v0.35.1/go.mod h1:HI/6jXlwkiOL5zL9bqA3en1Ygv60F03oEpnuU1G56Bs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
diff --git a/jsonnet/generated/auth_proxy_client_clusterrole.json b/jsonnet/generated/auth_proxy_client_clusterrole.json
index c0053ca9..79e4be27 100644
--- a/jsonnet/generated/auth_proxy_client_clusterrole.json
+++ b/jsonnet/generated/auth_proxy_client_clusterrole.json
@@ -3,7 +3,7 @@
"kind": "ClusterRole",
"metadata": {
"labels": {
- "app.kubernetes.io/component": "kube-rbac-proxy",
+ "app.kubernetes.io/component": "metrics",
"app.kubernetes.io/created-by": "perses-operator",
"app.kubernetes.io/instance": "metrics-reader",
"app.kubernetes.io/name": "clusterrole",
diff --git a/jsonnet/generated/auth_proxy_role.json b/jsonnet/generated/auth_proxy_role.json
index 91ce4ddf..f83fcede 100644
--- a/jsonnet/generated/auth_proxy_role.json
+++ b/jsonnet/generated/auth_proxy_role.json
@@ -3,7 +3,7 @@
"kind": "ClusterRole",
"metadata": {
"labels": {
- "app.kubernetes.io/component": "kube-rbac-proxy",
+ "app.kubernetes.io/component": "metrics",
"app.kubernetes.io/created-by": "perses-operator",
"app.kubernetes.io/instance": "proxy-role",
"app.kubernetes.io/name": "clusterrole",
diff --git a/jsonnet/generated/auth_proxy_role_binding.json b/jsonnet/generated/auth_proxy_role_binding.json
index d3598d0d..3a2b8d55 100644
--- a/jsonnet/generated/auth_proxy_role_binding.json
+++ b/jsonnet/generated/auth_proxy_role_binding.json
@@ -3,7 +3,7 @@
"kind": "ClusterRoleBinding",
"metadata": {
"labels": {
- "app.kubernetes.io/component": "kube-rbac-proxy",
+ "app.kubernetes.io/component": "metrics",
"app.kubernetes.io/created-by": "perses-operator",
"app.kubernetes.io/instance": "proxy-rolebinding",
"app.kubernetes.io/name": "clusterrolebinding",
diff --git a/jsonnet/generated/auth_proxy_service.json b/jsonnet/generated/auth_proxy_service.json
index c6548166..86da808a 100644
--- a/jsonnet/generated/auth_proxy_service.json
+++ b/jsonnet/generated/auth_proxy_service.json
@@ -3,7 +3,7 @@
"kind": "Service",
"metadata": {
"labels": {
- "app.kubernetes.io/component": "kube-rbac-proxy",
+ "app.kubernetes.io/component": "metrics",
"app.kubernetes.io/created-by": "perses-operator",
"app.kubernetes.io/instance": "controller-manager-metrics-service",
"app.kubernetes.io/name": "service",
diff --git a/main.go b/main.go
index add79549..85b9b343 100644
--- a/main.go
+++ b/main.go
@@ -32,6 +32,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -63,6 +64,7 @@ func init() {
func main() {
var metricsAddr string
+ var secureMetrics bool
var enableLeaderElection bool
var probeAddr string
var persesImage string
@@ -71,7 +73,8 @@ func main() {
var webhookPort int
var certDir string
- flag.StringVar(&metricsAddr, "metrics-bind-address", ":8082", "The address the metric endpoint binds to.")
+ flag.StringVar(&metricsAddr, "metrics-bind-address", ":8443", "The address the metrics endpoint binds to. Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
+ flag.BoolVar(&secureMetrics, "metrics-secure", true, "If the metrics endpoint should be served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook server binds to.")
flag.StringVar(&certDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs", "The directory where webhook TLS certificates are stored.")
@@ -89,23 +92,20 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
- disableHTTP2 := func(c *tls.Config) {
- if enableHTTP2 {
- return
- }
- c.NextProtos = []string{"http/1.1"}
+ tlsOpts := []func(*tls.Config){}
+ if !enableHTTP2 {
+ tlsOpts = append(tlsOpts, func(c *tls.Config) {
+ c.NextProtos = []string{"http/1.1"}
+ })
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- Metrics: server.Options{
- BindAddress: metricsAddr,
- TLSOpts: []func(*tls.Config){disableHTTP2},
- },
+ Scheme: scheme,
+ Metrics: buildMetricsServerOptions(metricsAddr, secureMetrics, tlsOpts),
WebhookServer: webhook.NewServer(webhook.Options{
Port: webhookPort,
CertDir: certDir,
- TLSOpts: []func(*tls.Config){disableHTTP2},
+ TLSOpts: tlsOpts,
}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
@@ -238,3 +238,17 @@ func main() {
os.Exit(1)
}
}
+
+func buildMetricsServerOptions(bindAddress string, secureMetrics bool, tlsOpts []func(*tls.Config)) server.Options {
+ metricsServerOptions := server.Options{
+ BindAddress: bindAddress,
+ SecureServing: secureMetrics,
+ TLSOpts: tlsOpts,
+ }
+
+ if secureMetrics {
+ metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+ }
+
+ return metricsServerOptions
+}
diff --git a/main_test.go b/main_test.go
new file mode 100644
index 00000000..6882fd8c
--- /dev/null
+++ b/main_test.go
@@ -0,0 +1,42 @@
+// Copyright The Perses Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "crypto/tls"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBuildMetricsServerOptions_SecureEnabled(t *testing.T) {
+ tlsOpts := []func(*tls.Config){
+ func(c *tls.Config) { c.NextProtos = []string{"http/1.1"} },
+ }
+
+ opts := buildMetricsServerOptions(":8443", true, tlsOpts)
+
+ assert.Equal(t, ":8443", opts.BindAddress)
+ assert.True(t, opts.SecureServing)
+ assert.NotNil(t, opts.FilterProvider, "FilterProvider should be set when secure metrics is enabled")
+ assert.Len(t, opts.TLSOpts, 1)
+}
+
+func TestBuildMetricsServerOptions_SecureDisabled(t *testing.T) {
+ opts := buildMetricsServerOptions(":8080", false, nil)
+
+ assert.Equal(t, ":8080", opts.BindAddress)
+ assert.False(t, opts.SecureServing)
+ assert.Nil(t, opts.FilterProvider, "FilterProvider should not be set when secure metrics is disabled")
+}
diff --git a/scripts/generate-metrics-docs/main.go b/scripts/generate-metrics-docs/main.go
index d818fa7f..4e419c48 100644
--- a/scripts/generate-metrics-docs/main.go
+++ b/scripts/generate-metrics-docs/main.go
@@ -102,15 +102,16 @@ The Perses Operator exposes Prometheus metrics for monitoring operator health an
## Accessing Metrics
-Metrics are exposed on port ` + "`8082`" + ` at the ` + "`/metrics`" + ` endpoint:
+Metrics are exposed on port ` + "`8443`" + ` over HTTPS at the ` + "`/metrics`" + ` endpoint with authentication and authorization enabled:
` + "```bash" + `
# Port forward to the operator pod
kubectl port-forward -n perses-operator-system \
- deployment/perses-operator-controller-manager 8082:8082
+ deployment/perses-operator-controller-manager 8443:8443
-# View metrics
-curl http://localhost:8082/metrics
+# View metrics (requires a valid bearer token)
+curl -sk https://localhost:8443/metrics \
+ -H "Authorization: Bearer $(kubectl create token -n perses-operator-system perses-operator-controller-manager)"
` + "```" + `
## Available Metrics
diff --git a/test/e2e/metrics-auth/00-assert.yaml b/test/e2e/metrics-auth/00-assert.yaml
new file mode 100644
index 00000000..ada186bf
--- /dev/null
+++ b/test/e2e/metrics-auth/00-assert.yaml
@@ -0,0 +1,85 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestAssert
+timeout: 30
+commands:
+ # Verify operator deployment has no kube-rbac-proxy sidecar container
+ - script: |
+ containers=$(kubectl get deployment perses-operator-controller-manager \
+ -n perses-operator-system \
+ -o jsonpath='{.spec.template.spec.containers[*].name}')
+ if echo "$containers" | grep -q "kube-rbac-proxy"; then
+ echo "FAIL: kube-rbac-proxy sidecar should not exist, found containers: $containers"
+ exit 1
+ fi
+ echo "PASS: no kube-rbac-proxy sidecar found"
+
+ # Verify manager container exposes the metrics port (8443)
+ - script: |
+ port=$(kubectl get deployment perses-operator-controller-manager \
+ -n perses-operator-system \
+ -o jsonpath='{.spec.template.spec.containers[?(@.name=="manager")].ports[?(@.name=="https")].containerPort}')
+ if [ "$port" != "8443" ]; then
+ echo "FAIL: expected metrics containerPort 8443, got '$port'"
+ exit 1
+ fi
+ echo "PASS: manager container exposes port 8443"
+
+ # Verify manager has --metrics-secure=true and --metrics-bind-address=:8443 args
+ - script: |
+ args=$(kubectl get deployment perses-operator-controller-manager \
+ -n perses-operator-system \
+ -o jsonpath='{.spec.template.spec.containers[?(@.name=="manager")].args}')
+ if ! echo "$args" | grep -q "metrics-secure=true"; then
+ echo "FAIL: expected --metrics-secure=true in args, got: $args"
+ exit 1
+ fi
+ if ! echo "$args" | grep -q "metrics-bind-address=:8443"; then
+ echo "FAIL: expected --metrics-bind-address=:8443 in args, got: $args"
+ exit 1
+ fi
+ echo "PASS: metrics args are correct"
+
+ # Verify metrics Service exists and exposes port 8443
+ - script: |
+ port=$(kubectl get service perses-operator-controller-manager-metrics-service \
+ -n perses-operator-system \
+ -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
+ if [ "$port" != "8443" ]; then
+ echo "FAIL: expected service port 8443, got '$port'"
+ exit 1
+ fi
+ echo "PASS: metrics service exposes port 8443"
+
+ # Verify proxy-role ClusterRole exists with correct RBAC rules
+ - script: |
+ rules=$(kubectl get clusterrole perses-operator-proxy-role \
+ -o jsonpath='{.rules}')
+ if ! echo "$rules" | grep -q "tokenreviews"; then
+ echo "FAIL: proxy-role missing tokenreviews permission"
+ exit 1
+ fi
+ if ! echo "$rules" | grep -q "subjectaccessreviews"; then
+ echo "FAIL: proxy-role missing subjectaccessreviews permission"
+ exit 1
+ fi
+ echo "PASS: proxy-role has correct RBAC rules"
+
+ # Verify proxy-rolebinding references the correct role
+ - script: |
+ role=$(kubectl get clusterrolebinding perses-operator-proxy-rolebinding \
+ -o jsonpath='{.roleRef.name}')
+ if [ "$role" != "perses-operator-proxy-role" ]; then
+ echo "FAIL: expected roleRef perses-operator-proxy-role, got '$role'"
+ exit 1
+ fi
+ echo "PASS: proxy-rolebinding references correct role"
+
+ # Verify metrics-reader ClusterRole exists with /metrics access
+ - script: |
+ urls=$(kubectl get clusterrole perses-operator-metrics-reader \
+ -o jsonpath='{.rules[0].nonResourceURLs}')
+ if ! echo "$urls" | grep -q "/metrics"; then
+ echo "FAIL: metrics-reader missing /metrics URL"
+ exit 1
+ fi
+ echo "PASS: metrics-reader grants /metrics access"