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"