diff --git a/.gitignore b/.gitignore index 9b93a583..264894fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,31 @@ -bin/ -tmp/ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin/* +tmp/* +Dockerfile.cross + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Go workspace file +go.work + +# Kubernetes Generated files - skip generated files, except for vendored files +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +.vscode +*.swp +*.swo +*~ # Downloaded chart dependencies **/charts/*.tgz diff --git a/Dockerfile b/Dockerfile index 2f7a0ea8..c8ecbad5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,11 +3,14 @@ FROM --platform=$BUILDPLATFORM golang:1.23.3-bookworm@sha256:3f3b9daa3de608f3e869cd2ff8baf21555cf0fca9fd34251b8f340f9b7c30ec5 AS builder WORKDIR /app - +# Copy the Go Modules manifests COPY go.mod go.sum ./ COPY ./interop/ ./interop/ +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer RUN go mod download +# Copy the go source COPY ./src/ ./src/ COPY Makefile . @@ -19,8 +22,8 @@ ARG K8S_AGENTS_OPERATOR_VERSION="development" RUN make build K8S_AGENTS_OPERATOR_VERSION="${K8S_AGENTS_OPERATOR_VERSION}" -# Use minimal base image to package the operator -# Source: https://github.com/GoogleContainerTools/distroless +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot WORKDIR /bin COPY --from=builder /app/bin/operator . diff --git a/Makefile b/Makefile index 4429cada..475ae62b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ # Directories -GO_DIR = ./src BIN_DIR = ./bin TMP_DIR = $(shell pwd)/tmp @@ -12,19 +11,19 @@ K8S_AGENTS_OPERATOR_VERSION = "" .DEFAULT_GOAL := help # Go packages to test -TEST_PACKAGES = ./src/internal/config \ +TEST_PACKAGES = ./src/internal/apm \ + ./src/internal/autodetect \ + ./src/internal/config \ + ./src/internal/instrumentation \ + ./src/internal/migrate/upgrade \ ./src/internal/version \ - ./src/internal/webhookhandler \ - ./src/api/v1alpha2 \ - ./src/autodetect \ - ./src/instrumentation/ \ - ./src/instrumentation/upgrade \ - ./src/apm + ./src/internal/webhook \ + ./src/api/v1alpha2 # Kubebuilder variables SETUP_ENVTEST = $(LOCALBIN)/setup-envtest SETUP_ENVTEST_VERSION ?= release-0.19 -SETUP_ENVTEST_K8S_VERSION ?= 1.29.0 +SETUP_ENVTEST_K8S_VERSION ?= 1.30.0 ALL_SETUP_ENVTEST_K8S_VERSIONS ?= 1.30.0 1.29.3 1.28.3 1.27.1 1.26.1 #https://storage.googleapis.com/kubebuilder-tools ## Tool Versions @@ -89,13 +88,13 @@ coverprofile: $(TMP_DIR)/cover.out ## Generate coverage report go-test: $(SETUP_ENVTEST) $(TMP_DIR) ## Run Go tests with k8s version specified by $SETUP_ENVTEST_K8S_VERSION @chmod -R 755 $(LOCALBIN)/k8s KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use $(SETUP_ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \ - go test -v -cover -covermode=count -coverprofile=$(TMP_DIR)/cover.out $(TEST_PACKAGES) + go test -v -cover -covermode=count -coverprofile=$(TMP_DIR)/cover.out -coverpkg=./src/... $(TEST_PACKAGES) .PHONY: go-test-race go-test-race: $(SETUP_ENVTEST) $(TMP_DIR) ## Run Go tests with k8s version specified by $SETUP_ENVTEST_K8S_VERSION with race detector @chmod -R 755 $(LOCALBIN)/k8s KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use $(SETUP_ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \ - go test -v -race -cover -covermode=atomic -coverprofile=$(TMP_DIR)/cover.out $(TEST_PACKAGES) + go test -v -race -cover -covermode=atomic -coverprofile=$(TMP_DIR)/cover.out -coverpkg=./src/... $(TEST_PACKAGES) .PHONY: all-go-tests all-go-tests: ## Run go tests with all k8s versions specified by $ALL_SETUP_ENVTEST_K8S_VERSIONS @@ -160,10 +159,10 @@ go-format: ## Format all go files .PHONY: build build: ## Build the go binary - CGO_ENABLED=0 go build -ldflags="-X 'github.com/newrelic/k8s-agents-operator/src/internal/version.version=$(K8S_AGENTS_OPERATOR_VERSION)' -X 'github.com/newrelic/k8s-agents-operator/src/internal/version.buildDate=$(shell date)'" -o $(BIN_DIR)/operator $(GO_DIR) + CGO_ENABLED=0 go build -ldflags="-X 'github.com/newrelic/k8s-agents-operator/src/internal/version.version=$(K8S_AGENTS_OPERATOR_VERSION)' -X 'github.com/newrelic/k8s-agents-operator/src/internal/version.buildDate=$(shell date)'" -o $(BIN_DIR)/operator src/main.go -.PHONY: dockerbuild -dockerbuild: ## Build the docker image +.PHONY: docker-build +docker-build: ## Build the docker image DOCKER_BUILDKIT=1 docker build -t k8s-agent-operator:latest \ --platform=linux/amd64,linux/arm64,linux/arm \ . @@ -226,7 +225,7 @@ generate: controller-gen ## Generate stuff $(CONTROLLER_GEN) object:headerFile="boilerplate.txt" paths="./..." .PHONY: manifests -manifests: generate controller-gen +manifests: generate controller-gen ## Generate manifests $(CONTROLLER_GEN) $(CRD_OPTIONS) webhook paths="./..." \ rbac:roleName=manager-role output:rbac:artifacts:config=config/rbac \ output:webhook:artifacts:config=config/webhook \ @@ -237,5 +236,4 @@ manifests: generate controller-gen run-helmify: manifests helmify kustomize ## Generate the CRD with kustomize and helmify from the manifests @# could we do more here? $(KUSTOMIZE) build config/default | $(HELMIFY) tmp/k8s-agents-operator - cp ./tmp/k8s-agents-operator/templates/instrumentation-crd.yaml ./charts/k8s-agents-operator/templates/instrumentation-crd.yaml printf "\nIMPORTANT: The generated chart needs to be transformed!\n- deployment.yaml is split into deployment.yaml and service-account.yaml\n- mutating-webhook-configuration.yaml and validating-webhook-configuration.yaml are merged into service-account.yaml\n- Documents generated are missing several config options (i.e. labels)\n" diff --git a/charts/k8s-agents-operator/README.md b/charts/k8s-agents-operator/README.md index 107d1689..91896589 100644 --- a/charts/k8s-agents-operator/README.md +++ b/charts/k8s-agents-operator/README.md @@ -245,18 +245,13 @@ If you want to see a list of all available charts and releases, check [index.yam | admissionWebhooks.keyFile | string | `""` | Path to your own PEM-encoded private key. | | affinity | object | `{}` | Sets all pods' affinities. Can be configured also with `global.affinity` | | containerSecurityContext | object | `{}` | Sets all security context (at container level). Can be configured also with `global.securityContext.container` | -| controllerManager.kubeRbacProxy.containerSecurityContext | object | `{}` | Sets security context (at container level) for kubeRbacProxy. Overrides `containerSecurityContext` and `global.containerSecurityContext` | -| controllerManager.kubeRbacProxy.image.repository | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | Sets the repository and image to use for kube-rbac-proxy. Please ensure you're using a trusted image. | -| controllerManager.kubeRbacProxy.image.version | string | `"sha256:771a9a173e033a3ad8b46f5c00a7036eaa88c8d8d1fbd89217325168998113ea"` | Sets the kube-rbac-proxy image version to retrieve. Could be a tag i.e. "v0.16.0" or a SHA digest i.e. "sha256:771a9a173e033a3ad8b46f5c00a7036eaa88c8d8d1fbd89217325168998113ea" | -| controllerManager.kubeRbacProxy.resources.limits.cpu | string | `"500m"` | | -| controllerManager.kubeRbacProxy.resources.limits.memory | string | `"128Mi"` | | -| controllerManager.kubeRbacProxy.resources.requests.cpu | string | `"5m"` | | -| controllerManager.kubeRbacProxy.resources.requests.memory | string | `"64Mi"` | | -| controllerManager.manager.containerSecurityContext | object | `{}` | Sets security context (at container level) for the manager. Overrides `containerSecurityContext` and `global.containerSecurityContext` | +| controllerManager.manager.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}` | Sets security context (at container level) for the manager. Overrides `containerSecurityContext` and `global.containerSecurityContext` | | controllerManager.manager.image.pullPolicy | string | `nil` | | | controllerManager.manager.image.repository | string | `"newrelic/k8s-agents-operator"` | Sets the repository and image to use for the manager. Please ensure you're using trusted New Relic images. | | controllerManager.manager.image.version | string | `nil` | Sets the manager image version to retrieve. Could be a tag i.e. "v0.17.0" or a SHA digest i.e. "sha256:e2399e70e99ac370ca6a3c7e5affa9655da3b246d0ada77c40ed155b3726ee2e" | | controllerManager.manager.leaderElection | object | `{"enabled":true}` | Enable leader election mechanism for protecting against split brain if multiple operator pods/replicas are started | +| controllerManager.manager.resources.limits.cpu | string | `"500m"` | | +| controllerManager.manager.resources.limits.memory | string | `"192Mi"` | | | controllerManager.manager.resources.requests.cpu | string | `"100m"` | | | controllerManager.manager.resources.requests.memory | string | `"64Mi"` | | | controllerManager.replicas | int | `1` | | @@ -267,12 +262,12 @@ If you want to see a list of all available charts and releases, check [index.yam | metricsService.ports[0].name | string | `"https"` | | | metricsService.ports[0].port | int | `8443` | | | metricsService.ports[0].protocol | string | `"TCP"` | | -| metricsService.ports[0].targetPort | string | `"https"` | | +| metricsService.ports[0].targetPort | int | `8443` | | | metricsService.type | string | `"ClusterIP"` | | | nodeSelector | object | `{}` | Sets all pods' node selector. Can be configured also with `global.nodeSelector` | | podAnnotations | object | `{}` | Annotations to be added to the deployment. | | podLabels | object | `{}` | Additional labels for chart pods | -| podSecurityContext | object | `{"fsGroup":65532,"runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532}` | SecurityContext holds pod-level security attributes and common container settings | +| podSecurityContext | object | `{"runAsNonRoot":true}` | SecurityContext holds pod-level security attributes and common container settings | | priorityClassName | string | `""` | Sets pod's priorityClassName. Can be configured also with `global.priorityClassName` | | serviceAccount | object | See `values.yaml` | Settings controlling ServiceAccount creation | | serviceAccount.create | bool | `true` | Specifies whether a ServiceAccount should be created | diff --git a/charts/k8s-agents-operator/templates/_helpers.tpl b/charts/k8s-agents-operator/templates/_helpers.tpl index 9e57804b..a414e412 100644 --- a/charts/k8s-agents-operator/templates/_helpers.tpl +++ b/charts/k8s-agents-operator/templates/_helpers.tpl @@ -14,12 +14,3 @@ Returns if the template should render, it checks if the required values are set. {{- printf "%s:%s" .Values.controllerManager.manager.image.repository $managerVersion -}} {{- end -}} {{- end -}} - -{{- define "k8s-agents-operator.kubeRbacProxy.image" -}} -{{- $kubeRbacProxyVersion := .Values.controllerManager.kubeRbacProxy.image.version | default .Chart.AppVersion -}} -{{- if eq (substr 0 7 $kubeRbacProxyVersion) "sha256:" -}} -{{- printf "%s@%s" .Values.controllerManager.kubeRbacProxy.image.repository $kubeRbacProxyVersion -}} -{{- else -}} -{{- printf "%s:%s" .Values.controllerManager.kubeRbacProxy.image.repository $kubeRbacProxyVersion -}} -{{- end -}} -{{- end -}} \ No newline at end of file diff --git a/charts/k8s-agents-operator/templates/_naming.tpl b/charts/k8s-agents-operator/templates/_naming.tpl index 39498726..073e4f45 100644 --- a/charts/k8s-agents-operator/templates/_naming.tpl +++ b/charts/k8s-agents-operator/templates/_naming.tpl @@ -7,6 +7,10 @@ {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "webhook-service") -}} {{- end -}} +{{- define "k8s-agents-operator.metricsService.name" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "controller-manager-metrics-service") -}} +{{- end -}} + {{- define "k8s-agents-operator.webhook.mutating.name" -}} {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "mutation") -}} {{- end -}} @@ -23,12 +27,12 @@ {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "serving-cert") -}} {{- end -}} -{{- define "k8s-agents-operator.rbac.proxy.role.name" -}} -{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "proxy-role") -}} +{{- define "k8s-agents-operator.rbac.metricsAuth.role.name" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "metrics-auth-role") -}} {{- end -}} -{{- define "k8s-agents-operator.rbac.proxy.roleBinding.name" -}} -{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "proxy-rolebinding") -}} +{{- define "k8s-agents-operator.rbac.metricsAuth.roleBinding.name" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "metrics-auth-rolebinding") -}} {{- end -}} {{- define "k8s-agents-operator.rbac.manager.role.name" -}} @@ -50,3 +54,11 @@ {{- define "k8s-agents-operator.rbac.metricsReader.role.name" -}} {{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "metrics-reader") -}} {{- end -}} + +{{- define "k8s-agents-operator.rbac.instrumentationEditor.role.name" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "instrumentation-editor-role") -}} +{{- end -}} + +{{- define "k8s-agents-operator.rbac.instrumentationViewer.role.name" -}} +{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "instrumentation-viewer-role") -}} +{{- end -}} diff --git a/charts/k8s-agents-operator/templates/_security_context.tpl b/charts/k8s-agents-operator/templates/_security_context.tpl index a3b07971..a4533eec 100644 --- a/charts/k8s-agents-operator/templates/_security_context.tpl +++ b/charts/k8s-agents-operator/templates/_security_context.tpl @@ -1,14 +1,3 @@ -{{- /* -A helper to return the container security context to apply to kubeRbacProxy. -*/ -}} -{{- define "k8s-agents-operator.kubeRbacProxy.securityContext.container" -}} -{{- if .Values.controllerManager.kubeRbacProxy.containerSecurityContext -}} - {{- toYaml .Values.controllerManager.kubeRbacProxy.containerSecurityContext -}} -{{- else if include "newrelic.common.securityContext.container" . -}} - {{- include "newrelic.common.securityContext.container" . -}} -{{- end -}} -{{- end -}} - {{- /* A helper to return the container security context to apply to the manager. */ -}} diff --git a/charts/k8s-agents-operator/templates/deployment.yaml b/charts/k8s-agents-operator/templates/deployment.yaml index 0d7a56f8..dbc269be 100644 --- a/charts/k8s-agents-operator/templates/deployment.yaml +++ b/charts/k8s-agents-operator/templates/deployment.yaml @@ -42,12 +42,13 @@ spec: {{- . | nindent 10 }} {{- end }} args: - - --metrics-addr=127.0.0.1:8080 + - --metrics-bind-address=:8443 {{- if .Values.controllerManager.manager.leaderElection.enabled }} - - --enable-leader-election + - --leader-elect {{- end }} - - --zap-log-level=info - - --zap-time-encoding=rfc3339nano + - --health-probe-bind-address=:8081 + command: + - /bin/operator env: - name: OPERATOR_NAMESPACE valueFrom: @@ -81,26 +82,6 @@ spec: - mountPath: /tmp/k8s-webhook-server/serving-certs name: cert readOnly: true - - name: kube-rbac-proxy - {{- with include "k8s-agents-operator.kubeRbacProxy.securityContext.container" . }} - securityContext: - {{- . | nindent 10 }} - {{- end }} - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: {{ quote .Values.kubernetesClusterDomain }} - image: {{ include "k8s-agents-operator.kubeRbacProxy.image" . }} - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - {{- toYaml .Values.controllerManager.kubeRbacProxy.resources | nindent 10 }} {{- if or .Values.admissionWebhooks.create (include "k8s-agents-operator.certificateSecret.name" . ) }} volumes: - name: cert diff --git a/charts/k8s-agents-operator/templates/instrumentation-crd.yaml b/charts/k8s-agents-operator/templates/instrumentation-crd.yaml index 68dbde93..c3762cbd 100644 --- a/charts/k8s-agents-operator/templates/instrumentation-crd.yaml +++ b/charts/k8s-agents-operator/templates/instrumentation-crd.yaml @@ -3,10 +3,12 @@ kind: CustomResourceDefinition metadata: name: instrumentations.newrelic.com annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.4 labels: {{- include "newrelic.common.labels" . | nindent 4 }} spec: + conversion: + strategy: None group: newrelic.com names: kind: Instrumentation @@ -85,10 +87,13 @@ spec: description: The key to select. type: string name: + default: "" description: |- 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 - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap or its @@ -147,10 +152,13 @@ spec: be a valid secret key. type: string name: + default: "" description: |- 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 - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key @@ -179,11 +187,9 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. - This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. @@ -194,6 +200,12 @@ spec: the Pod where this field is used. It makes that resource available inside a container. type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string required: - name type: object @@ -222,7 +234,7 @@ spec: description: |- Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. + otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object @@ -278,11 +290,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -323,11 +337,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -404,4 +420,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] \ No newline at end of file + storedVersions: [] diff --git a/charts/k8s-agents-operator/templates/instrumentation-editor-rbac.yaml b/charts/k8s-agents-operator/templates/instrumentation-editor-rbac.yaml new file mode 100644 index 00000000..bf9ca460 --- /dev/null +++ b/charts/k8s-agents-operator/templates/instrumentation-editor-rbac.yaml @@ -0,0 +1,26 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "k8s-agents-operator.rbac.instrumentationEditor.role.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: +- apiGroups: + - newrelic.com + resources: + - instrumentations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - newrelic.com + resources: + - instrumentations/status + verbs: + - get diff --git a/charts/k8s-agents-operator/templates/instrumentation-viewer-rbac.yaml b/charts/k8s-agents-operator/templates/instrumentation-viewer-rbac.yaml new file mode 100644 index 00000000..1de332aa --- /dev/null +++ b/charts/k8s-agents-operator/templates/instrumentation-viewer-rbac.yaml @@ -0,0 +1,22 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "k8s-agents-operator.rbac.instrumentationViewer.role.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "newrelic.common.labels" . | nindent 4 }} +rules: +- apiGroups: + - newrelic.com + resources: + - instrumentations + verbs: + - get + - list + - watch +- apiGroups: + - newrelic.com + resources: + - instrumentations/status + verbs: + - get diff --git a/charts/k8s-agents-operator/templates/leader-election-rbac.yaml b/charts/k8s-agents-operator/templates/leader-election-rbac.yaml index 65111dd9..dacf9abc 100644 --- a/charts/k8s-agents-operator/templates/leader-election-rbac.yaml +++ b/charts/k8s-agents-operator/templates/leader-election-rbac.yaml @@ -19,13 +19,17 @@ rules: - patch - delete - apiGroups: - - "" + - coordination.k8s.io resources: - - configmaps/status + - leases verbs: - get + - list + - watch + - create - update - patch + - delete - apiGroups: - "" resources: diff --git a/charts/k8s-agents-operator/templates/manager-rbac.yaml b/charts/k8s-agents-operator/templates/manager-rbac.yaml index a292d931..ace08b44 100644 --- a/charts/k8s-agents-operator/templates/manager-rbac.yaml +++ b/charts/k8s-agents-operator/templates/manager-rbac.yaml @@ -12,17 +12,6 @@ rules: verbs: - create - patch -- apiGroups: [ "" ] - resources: ["secrets"] - verbs: - - get - - list - - create - - delete - - deletecollection - - patch - - update - - watch - apiGroups: - "" resources: @@ -30,6 +19,19 @@ rules: verbs: - list - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch - apiGroups: - apps resources: @@ -39,24 +41,31 @@ rules: - list - watch - apiGroups: - - coordination.k8s.io + - newrelic.com resources: - - leases + - instrumentations verbs: - create + - delete - get - list + - patch - update + - watch - apiGroups: - newrelic.com resources: - - instrumentations + - instrumentations/finalizers + verbs: + - update +- apiGroups: + - newrelic.com + resources: + - instrumentations/status verbs: - get - - list - patch - update - - watch - apiGroups: - route.openshift.io resources: diff --git a/charts/k8s-agents-operator/templates/proxy-rbac.yaml b/charts/k8s-agents-operator/templates/metrics-auth-rbac.yaml similarity index 73% rename from charts/k8s-agents-operator/templates/proxy-rbac.yaml rename to charts/k8s-agents-operator/templates/metrics-auth-rbac.yaml index 47f30092..08561f32 100644 --- a/charts/k8s-agents-operator/templates/proxy-rbac.yaml +++ b/charts/k8s-agents-operator/templates/metrics-auth-rbac.yaml @@ -1,7 +1,8 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: {{ include "k8s-agents-operator.rbac.proxy.role.name" . }} + name: {{ include "k8s-agents-operator.rbac.metricsAuth.role.name" . }} + namespace: {{ .Release.Namespace }} labels: {{- include "newrelic.common.labels" . | nindent 4 }} rules: @@ -21,14 +22,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: {{ include "k8s-agents-operator.rbac.proxy.roleBinding.name" . }} + name: {{ include "k8s-agents-operator.rbac.metricsAuth.roleBinding.name" . }} namespace: {{ .Release.Namespace }} labels: {{- include "newrelic.common.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ include "k8s-agents-operator.rbac.proxy.role.name" . }} + name: {{ include "k8s-agents-operator.rbac.metricsAuth.role.name" . }} subjects: - kind: ServiceAccount name: {{ include "newrelic.common.serviceAccount.name" . }} diff --git a/charts/k8s-agents-operator/templates/metrics-service.yaml b/charts/k8s-agents-operator/templates/metrics-service.yaml index 427f60f0..d0cf7f7d 100644 --- a/charts/k8s-agents-operator/templates/metrics-service.yaml +++ b/charts/k8s-agents-operator/templates/metrics-service.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "newrelic.common.naming.fullname" . }} + name: {{ include "k8s-agents-operator.metricsService.name" . }} namespace: {{ .Release.Namespace }} labels: control-plane: controller-manager diff --git a/charts/k8s-agents-operator/templates/webhook-configuration.yaml b/charts/k8s-agents-operator/templates/webhook-configuration.yaml index a3294106..058a5472 100644 --- a/charts/k8s-agents-operator/templates/webhook-configuration.yaml +++ b/charts/k8s-agents-operator/templates/webhook-configuration.yaml @@ -88,7 +88,7 @@ metadata: {{- include "newrelic.common.labels" . | nindent 4 }} webhooks: - admissionReviewVersions: - - v1 + - v1 clientConfig: {{- if .Values.admissionWebhooks.autoGenerateCert.enabled }} caBundle: {{ $tls.caCert }} @@ -100,18 +100,18 @@ webhooks: failurePolicy: Fail name: vinstrumentationcreateupdate.kb.io rules: - - apiGroups: - - newrelic.com - apiVersions: - - v1alpha2 - operations: - - CREATE - - UPDATE - resources: - - instrumentations + - apiGroups: + - newrelic.com + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - instrumentations sideEffects: None - admissionReviewVersions: - - v1 + - v1 clientConfig: {{- if .Values.admissionWebhooks.autoGenerateCert.enabled }} caBundle: {{ $tls.caCert }} @@ -123,12 +123,12 @@ webhooks: failurePolicy: Ignore name: vinstrumentationdelete.kb.io rules: - - apiGroups: - - newrelic.com - apiVersions: - - v1alpha2 - operations: - - DELETE - resources: - - instrumentations + - apiGroups: + - newrelic.com + apiVersions: + - v1alpha2 + operations: + - DELETE + resources: + - instrumentations sideEffects: None diff --git a/charts/k8s-agents-operator/tests/images_test.yaml b/charts/k8s-agents-operator/tests/images_test.yaml index ac47fda6..d786e9c7 100644 --- a/charts/k8s-agents-operator/tests/images_test.yaml +++ b/charts/k8s-agents-operator/tests/images_test.yaml @@ -25,10 +25,6 @@ tests: image: repository: nr/test-1 version: "1.1.1" - kubeRbacProxy: - image: - repository: nr/test-2 - version: "1.1.2" asserts: - equal: path: spec.template.spec.containers[0].image @@ -46,10 +42,6 @@ tests: image: repository: nr/test-1 version: "sha256:e2399e70e99ac370ca6a3c7e5affa9655da3b246d0ada77c40ed155b3726ee2e" - kubeRbacProxy: - image: - repository: nr/test-2 - version: "sha256:771a9a173e033a3ad8b46f5c00a7036eaa88c8d8d1fbd89217325168998113ea" asserts: - equal: path: spec.template.spec.containers[0].image diff --git a/charts/k8s-agents-operator/tests/security_context_test.yaml b/charts/k8s-agents-operator/tests/security_context_test.yaml index aa34c65f..e671d40d 100644 --- a/charts/k8s-agents-operator/tests/security_context_test.yaml +++ b/charts/k8s-agents-operator/tests/security_context_test.yaml @@ -140,19 +140,6 @@ tests: value: managerKey: managerValue template: templates/deployment.yaml - - it: sets container securityContext from kubeRbacProxy values - set: - licenseKey: us-whatever - controllerManager: - kubeRbacProxy: - containerSecurityContext: - kubeRbacProxyKey: kubeRbacProxyValue - asserts: - - equal: - path: spec.template.spec.containers[1].securityContext - value: - kubeRbacProxyKey: kubeRbacProxyValue - template: templates/deployment.yaml - it: sets container securityContext from manager values overriding top level and global values set: licenseKey: us-whatever @@ -170,22 +157,4 @@ tests: path: spec.template.spec.containers[0].securityContext value: managerKey: managerValue - template: templates/deployment.yaml - - it: sets container securityContext from kubeRbacProxy values overriding top level and global values - set: - licenseKey: us-whatever - containerSecurityContext: - topLevelKey: topLevelValue - global: - containerSecurityContext: - globalKey: globalValue - controllerManager: - kubeRbacProxy: - containerSecurityContext: - kubeRbacProxyKey: kubeRbacProxyValue - asserts: - - equal: - path: spec.template.spec.containers[1].securityContext - value: - kubeRbacProxyKey: kubeRbacProxyValue template: templates/deployment.yaml \ No newline at end of file diff --git a/charts/k8s-agents-operator/values.yaml b/charts/k8s-agents-operator/values.yaml index ce6fc27d..88f7cd88 100644 --- a/charts/k8s-agents-operator/values.yaml +++ b/charts/k8s-agents-operator/values.yaml @@ -28,10 +28,10 @@ affinity: {} # -- Source: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ # -- SecurityContext holds pod-level security attributes and common container settings podSecurityContext: - runAsGroup: 65532 +# runAsGroup: 65532 runAsNonRoot: true - runAsUser: 65532 - fsGroup: 65532 +# runAsUser: 65532 +# fsGroup: 65532 # -- Sets all security context (at container level). Can be configured also with `global.securityContext.container` containerSecurityContext: {} @@ -48,32 +48,23 @@ controllerManager: version: pullPolicy: resources: + limits: + cpu: 500m + memory: 192Mi requests: cpu: 100m memory: 64Mi # -- Sets security context (at container level) for the manager. Overrides `containerSecurityContext` and `global.containerSecurityContext` - containerSecurityContext: {} + containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL # -- Source: https://docs.openshift.com/container-platform/4.10/operators/operator_sdk/osdk-leader-election.html # -- Enable leader election mechanism for protecting against split brain if multiple operator pods/replicas are started leaderElection: enabled: true - kubeRbacProxy: - image: - # -- Sets the repository and image to use for kube-rbac-proxy. Please ensure you're using a trusted image. - repository: gcr.io/kubebuilder/kube-rbac-proxy - # -- Sets the kube-rbac-proxy image version to retrieve. Could be a tag i.e. "v0.16.0" or a SHA digest i.e. "sha256:771a9a173e033a3ad8b46f5c00a7036eaa88c8d8d1fbd89217325168998113ea" - version: sha256:771a9a173e033a3ad8b46f5c00a7036eaa88c8d8d1fbd89217325168998113ea - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - # -- Sets security context (at container level) for kubeRbacProxy. Overrides `containerSecurityContext` and `global.containerSecurityContext` - containerSecurityContext: {} - # -- Settings controlling ServiceAccount creation # @default -- See `values.yaml` serviceAccount: @@ -90,7 +81,7 @@ metricsService: - name: https port: 8443 protocol: TCP - targetPort: https + targetPort: 8443 type: ClusterIP webhookService: diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index be1da1a9..753a7f4e 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -1,7 +1,6 @@ # The following manifests contain a self-signed issuer CR and a certificate CR. # More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for -# breaking changes +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. apiVersion: cert-manager.io/v1 kind: Issuer metadata: @@ -9,7 +8,6 @@ metadata: namespace: system spec: selfSigned: {} - --- apiVersion: cert-manager.io/v1 kind: Certificate diff --git a/config/crd/bases/newrelic.com_instrumentations.yaml b/config/crd/bases/newrelic.com_instrumentations.yaml index e4674b01..776765c4 100644 --- a/config/crd/bases/newrelic.com_instrumentations.yaml +++ b/config/crd/bases/newrelic.com_instrumentations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.4 name: instrumentations.newrelic.com spec: group: newrelic.com @@ -85,10 +85,13 @@ spec: description: The key to select. type: string name: + default: "" description: |- 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 - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap or its @@ -148,10 +151,13 @@ spec: be a valid secret key. type: string name: + default: "" description: |- 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 - TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or its key @@ -180,11 +186,9 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. - This field is immutable. It can only be set for containers. items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. @@ -195,6 +199,12 @@ spec: the Pod where this field is used. It makes that resource available inside a container. type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string required: - name type: object @@ -223,7 +233,7 @@ spec: description: |- Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. + otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object @@ -279,11 +289,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -324,11 +336,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index e37d2dd0..6d981f21 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,13 +5,19 @@ resources: - bases/newrelic.com_instrumentations.yaml # +kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD +- path: patches/webhook_in_instrumentations.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD +- path: patches/cainjection_in_instrumentations.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch +# [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. + configurations: - kustomizeconfig.yaml diff --git a/config/crd/patches/cainjection_in_instrumentations.yaml b/config/crd/patches/cainjection_in_instrumentations.yaml new file mode 100644 index 00000000..c1370b3a --- /dev/null +++ b/config/crd/patches/cainjection_in_instrumentations.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + name: instrumentations.newrelic.com diff --git a/config/crd/patches/webhook_in_instrumentations.yaml b/config/crd/patches/webhook_in_instrumentations.yaml new file mode 100644 index 00000000..4295d6be --- /dev/null +++ b/config/crd/patches/webhook_in_instrumentations.yaml @@ -0,0 +1,17 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: instrumentations.newrelic.com +spec: + conversion: + strategy: None +# strategy: Webhook +# webhook: +# clientConfig: +# service: +# namespace: system +# name: webhook-service +# path: /convert +# conversionReviewVersions: +# - v1alpha2 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 1fe12e78..0c9f3024 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -9,8 +9,10 @@ namespace: k8s-agents-operator-system namePrefix: k8s-agents-operator- # Labels to add to all resources and selectors. -commonLabels: - app.kubernetes.io/name: k8s-agents-operator +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue resources: - ../crd @@ -20,24 +22,21 @@ resources: - ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml 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 +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml target: - kind: Deployment + kind: Deployment - path: manager_webhook_patch.yaml target: kind: Deployment -- path: mutatingwebhook_patch.yaml - target: - kind: MutatingWebhookConfiguration -- path: validatingwebhook_patch.yaml - target: - kind: ValidatingWebhookConfiguration +- path: webhookcainjection_patch.yaml +# TODO: Investigate replacing vars # the following config is for teaching kustomize how to do var substitution vars: - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index f44f9c71..00000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# 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. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--metrics-addr=127.0.0.1:8080" - - "--enable-leader-election" - - "--zap-log-level=info" - - "--zap-time-encoding=rfc3339nano" diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 00000000..2aaef653 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 diff --git a/config/rbac/auth_proxy_service.yaml b/config/default/metrics_service.yaml similarity index 69% rename from config/rbac/auth_proxy_service.yaml rename to config/default/metrics_service.yaml index e5856d60..ef414742 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/default/metrics_service.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager name: controller-manager-metrics-service namespace: system @@ -10,8 +9,7 @@ spec: ports: - name: https port: 8443 - targetPort: https protocol: TCP + targetPort: 8443 selector: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager diff --git a/config/default/mutatingwebhook_patch.yaml b/config/default/mutatingwebhook_patch.yaml deleted file mode 100644 index 43d0d3c1..00000000 --- a/config/default/mutatingwebhook_patch.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - diff --git a/config/default/validatingwebhook_patch.yaml b/config/default/webhookcainjection_patch.yaml similarity index 62% rename from config/default/validatingwebhook_patch.yaml rename to config/default/webhookcainjection_patch.yaml index 47ef1d13..02ab515d 100644 --- a/config/default/validatingwebhook_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,6 +1,13 @@ # This patch add annotation to admission webhook config and # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 44027654..b70d9e44 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -2,10 +2,8 @@ apiVersion: v1 kind: Namespace metadata: labels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager name: system - --- apiVersion: apps/v1 kind: Deployment @@ -13,40 +11,66 @@ metadata: name: controller-manager namespace: system labels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager spec: selector: matchLabels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager replicas: 1 template: metadata: + annotations: + kubectl.kubernetes.io/default-container: manager labels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager spec: + securityContext: + runAsNonRoot: true + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault containers: - - args: - - --enable-leader-election - image: controller - name: manager - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - requests: - cpu: 100m - memory: 64Mi + - command: + - /bin/operator + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 192Mi + requests: + cpu: 100m + memory: 64Mi + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: ENABLE_WEBHOOKS + value: "true" serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/config/manifests/bases/newrelic-agent-operator.clusterserviceversion.yaml b/config/manifests/bases/newrelic-agent-operator.clusterserviceversion.yaml index 21c286cf..3b86ab4f 100644 --- a/config/manifests/bases/newrelic-agent-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/newrelic-agent-operator.clusterserviceversion.yaml @@ -47,8 +47,10 @@ spec: - name: Newrelic Agent Operator url: https://k8s-agents-operator.domain maintainers: - - email: alozoya@newrelic.com - name: Andrew Lozoya + - email: dbudziwojski@newrelic.com + name: Daniel Budziwojski + - email: danielstokes@newrelic.com + name: Daniel Stokes maturity: alpha provider: name: New Relic diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 63ca74d7..91194dd7 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -1,4 +1,5 @@ resources: +- bases/newrelic-agent-operator.clusterserviceversion.yaml - ../default - ../samples - ../scorecard diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index ee40dfb7..c03463d5 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -1,17 +1,15 @@ - # Prometheus Monitor Service (Metrics) apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager name: controller-manager-metrics-monitor namespace: system spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: @@ -22,5 +20,4 @@ spec: name: k8s-agents-operator-controller-manager-service-cert selector: matchLabels: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager diff --git a/config/rbac/instrumentation_editor_role.yaml b/config/rbac/instrumentation_editor_role.yaml new file mode 100644 index 00000000..2554d81b --- /dev/null +++ b/config/rbac/instrumentation_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit instrumentations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: instrumentation-editor-role +rules: +- apiGroups: + - newrelic.com + resources: + - instrumentations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - newrelic.com + resources: + - instrumentations/status + verbs: + - get diff --git a/config/rbac/instrumentation_viewer_role.yaml b/config/rbac/instrumentation_viewer_role.yaml new file mode 100644 index 00000000..bd132de8 --- /dev/null +++ b/config/rbac/instrumentation_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view instrumentations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: instrumentation-viewer-role +rules: +- apiGroups: + - newrelic.com + resources: + - instrumentations + verbs: + - get + - list + - watch +- apiGroups: + - newrelic.com + resources: + - instrumentations/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 27cb1d17..ca629473 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -1,13 +1,26 @@ resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. - service_account.yaml - role.yaml - role_binding.yaml - 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. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the Project itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- instrumentation_editor_role.yaml +- instrumentation_viewer_role.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 7dc16c42..4190ec80 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -17,13 +17,17 @@ rules: - patch - delete - apiGroups: - - "" + - coordination.k8s.io resources: - - configmaps/status + - leases verbs: - get + - list + - watch + - create - update - patch + - delete - apiGroups: - "" resources: diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/metrics_auth_role.yaml similarity index 50% rename from config/rbac/auth_proxy_role.yaml rename to config/rbac/metrics_auth_role.yaml index 618f5e41..32d2e4ec 100644 --- a/config/rbac/auth_proxy_role.yaml +++ b/config/rbac/metrics_auth_role.yaml @@ -1,13 +1,17 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: proxy-role + name: metrics-auth-role rules: -- apiGroups: ["authentication.k8s.io"] +- apiGroups: + - authentication.k8s.io resources: - tokenreviews - verbs: ["create"] -- apiGroups: ["authorization.k8s.io"] + verbs: + - create +- apiGroups: + - authorization.k8s.io resources: - subjectaccessreviews - verbs: ["create"] + verbs: + - create diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml similarity index 79% rename from config/rbac/auth_proxy_role_binding.yaml rename to config/rbac/metrics_auth_role_binding.yaml index ec7acc0a..e775d67f 100644 --- a/config/rbac/auth_proxy_role_binding.yaml +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -1,11 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: proxy-rolebinding + name: metrics-auth-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: proxy-role + name: metrics-auth-role subjects: - kind: ServiceAccount name: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/metrics_reader_role.yaml similarity index 66% rename from config/rbac/auth_proxy_client_clusterrole.yaml rename to config/rbac/metrics_reader_role.yaml index bd4af137..51a75db4 100644 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ b/config/rbac/metrics_reader_role.yaml @@ -3,5 +3,7 @@ kind: ClusterRole metadata: name: metrics-reader rules: -- nonResourceURLs: ["/metrics"] - verbs: ["get"] +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 242cff2c..c97fb684 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,7 +2,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: @@ -19,6 +18,19 @@ rules: verbs: - list - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch - apiGroups: - apps resources: @@ -28,24 +40,31 @@ rules: - list - watch - apiGroups: - - coordination.k8s.io + - newrelic.com resources: - - leases + - instrumentations verbs: - create + - delete - get - list + - patch - update + - watch - apiGroups: - newrelic.com resources: - - instrumentations + - instrumentations/finalizers + verbs: + - update +- apiGroups: + - newrelic.com + resources: + - instrumentations/status verbs: - get - - list - patch - update - - watch - apiGroups: - route.openshift.io resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 5d22abdd..fca389f8 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,3 +1,4 @@ -## This file is auto-generated, do not modify ## +## Append samples of your project ## resources: - instrumentation_v1alpha2_instrumentation.yaml +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index 50cd2d08..54e8aa50 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -1,16 +1,18 @@ resources: - bases/config.yaml -patchesJson6902: +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: - path: patches/basic.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config + version: v1alpha3 - path: patches/olm.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config -#+kubebuilder:scaffold:patchesJson6902 + version: v1alpha3 +# +kubebuilder:scaffold:patches diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index fb742649..84683cf8 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index adaaf65d..43f40a8b 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.27.0 + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-crds-have-validation-test @@ -22,9 +22,29 @@ path: /stages/0/tests/- value: entrypoint: - - scorecard-test - - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.27.0 + - scorecard-test + - olm-crds-have-resources + image: quay.io/operator-framework/scorecard-test:v1.38.0 labels: suite: olm test: olm-crds-have-resources-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-spec-descriptors + image: quay.io/operator-framework/scorecard-test:v1.38.0 + labels: + suite: olm + test: olm-spec-descriptors-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-status-descriptors + image: quay.io/operator-framework/scorecard-test:v1.38.0 + labels: + suite: olm + test: olm-status-descriptors-test diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml index 25e21e3c..206316e5 100644 --- a/config/webhook/kustomizeconfig.yaml +++ b/config/webhook/kustomizeconfig.yaml @@ -1,4 +1,4 @@ -# the following config is for teaching kustomize where to look at when substituting vars. +# the following config is for teaching kustomize where to look at when substituting nameReference. # It requires kustomize v2.1.0 or newer to work properly. nameReference: - kind: Service @@ -20,6 +20,3 @@ namespace: group: admissionregistration.k8s.io path: webhooks/clientConfig/service/namespace create: true - -varReference: -- path: metadata/annotations diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 15808428..4747cfa2 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -2,7 +2,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -45,12 +44,10 @@ webhooks: resources: - pods sideEffects: None - --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml index e0d6f15f..c9f09d4b 100644 --- a/config/webhook/service.yaml +++ b/config/webhook/service.yaml @@ -1,4 +1,3 @@ - apiVersion: v1 kind: Service metadata: @@ -7,8 +6,7 @@ metadata: spec: ports: - port: 443 - targetPort: 9443 protocol: TCP + targetPort: 9443 selector: - app.kubernetes.io/name: k8s-agents-operator control-plane: controller-manager diff --git a/go.mod b/go.mod index b9daeca5..eb0c4830 100644 --- a/go.mod +++ b/go.mod @@ -7,25 +7,29 @@ require ( github.com/google/go-cmp v0.6.0 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 - github.com/openshift/api v0.0.0-20241120064718-caf97963ed30 - github.com/spf13/pflag v1.0.5 + github.com/openshift/api v0.0.0-20241211151016-1a7b90faeadf github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel v1.32.0 k8s.io/api v0.31.2 k8s.io/apimachinery v0.31.2 k8s.io/client-go v0.31.2 - k8s.io/component-base v0.31.2 sigs.k8s.io/controller-runtime v0.19.1 ) require ( + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // 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.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect @@ -34,10 +38,12 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/imdario/mergo v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -54,27 +60,42 @@ require ( github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/apiserver v0.31.1 // indirect + k8s.io/component-base v0.31.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 // indirect k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 5d1a5db2..e19d450b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,13 @@ +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -13,12 +21,17 @@ 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.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/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.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -33,6 +46,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -41,12 +56,12 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241021161924-4cf4322d492d h1:dcUSYLuKITgwgLZJZpB+CKecsC8mXHhErghMX9ohbf4= -github.com/google/pprof v0.0.0-20241021161924-4cf4322d492d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -74,26 +89,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= -github.com/onsi/gomega v1.35.0 h1:xuM1M/UvMp9BCdS4hojhS9/4jEuVqS9Er3bqupeaoPM= -github.com/onsi/gomega v1.35.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/openshift/api v0.0.0-20241018083007-4f6053f954b0 h1:9CBNaPGycU2dDzq0XoRIqxH0vHZezKDfbINx8e5zH0I= -github.com/openshift/api v0.0.0-20241018083007-4f6053f954b0/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= -github.com/openshift/api v0.0.0-20241024191314-684b2b1679ba h1:jyeZlKhyeR9mdbQKbTl5oP5nz0vAoy1Q+xn+1PwgODE= -github.com/openshift/api v0.0.0-20241024191314-684b2b1679ba/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= -github.com/openshift/api v0.0.0-20241106222702-2429e35d6633 h1:JgeV16qlZA9YRrfPqElgfBlZaD0sgYBKI05rVWz45GY= -github.com/openshift/api v0.0.0-20241106222702-2429e35d6633/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= -github.com/openshift/api v0.0.0-20241107155230-d37bb9f7e380 h1:EstDpct2ypQv500NPRQk92YBk9ZO0baDd94mna8o6w0= -github.com/openshift/api v0.0.0-20241107155230-d37bb9f7e380/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= -github.com/openshift/api v0.0.0-20241120064718-caf97963ed30 h1:LrNXjO675I1FvPYE4P3UOFNlC0X37iKUUhgwk+huYnc= -github.com/openshift/api v0.0.0-20241120064718-caf97963ed30/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= +github.com/openshift/api v0.0.0-20241211151016-1a7b90faeadf h1:v0zcEm7GikYbs/N+YLLpKV75ASm7WpMRiSDZpvJvXnk= +github.com/openshift/api v0.0.0-20241211151016-1a7b90faeadf/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -114,22 +115,33 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= 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= @@ -154,6 +166,8 @@ golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -179,6 +193,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -188,27 +208,22 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= +k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= @@ -217,8 +232,8 @@ k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 h1:MErs8YA0abvOqJ8gIupA1T k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094/go.mod h1:7ioBJr1A6igWjsR2fxq2EZ0mlMwYLejazSIc2bzMp2U= k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= diff --git a/src/api/v1alpha2/instrumentation_types.go b/src/api/v1alpha2/instrumentation_types.go index e90626b9..c3bfb3a1 100644 --- a/src/api/v1alpha2/instrumentation_types.go +++ b/src/api/v1alpha2/instrumentation_types.go @@ -163,7 +163,7 @@ type Instrumentation struct { Status InstrumentationStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // InstrumentationList contains a list of Instrumentation type InstrumentationList struct { diff --git a/src/api/v1alpha2/instrumentation_webhook.go b/src/api/v1alpha2/instrumentation_webhook.go index b12eef74..21e36f45 100644 --- a/src/api/v1alpha2/instrumentation_webhook.go +++ b/src/api/v1alpha2/instrumentation_webhook.go @@ -16,6 +16,138 @@ limitations under the License. package v1alpha2 +import ( + "context" + "fmt" + "github.com/go-logr/logr" + "slices" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strings" +) + +// log is for logging in this package. +var instrumentationLog logr.Logger + +// SetupWebhookWithManager will set up the manager to manage the webhooks +func (r *Instrumentation) SetupWebhookWithManager(mgr ctrl.Manager, logger logr.Logger) error { + instrumentationLog = logger + return ctrl.NewWebhookManagedBy(mgr). + For(r). + WithValidator(r). + WithDefaulter(r). + Complete() +} + //+kubebuilder:webhook:path=/mutate-newrelic-com-v1alpha2-instrumentation,mutating=true,failurePolicy=fail,sideEffects=None,groups=newrelic.com,resources=instrumentations,verbs=create;update,versions=v1alpha2,name=minstrumentation.kb.io,admissionReviewVersions=v1 + +var _ webhook.CustomDefaulter = &Instrumentation{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *Instrumentation) Default(ctx context.Context, obj runtime.Object) error { + inst, ok := obj.(*Instrumentation) + if !ok { + return fmt.Errorf("expected an Instrumentation object but got %T", obj) + } + + instrumentationLog.Info("Defaulting for Instrumentation", "name", inst.GetName()) + if inst.Labels == nil { + inst.Labels = map[string]string{} + } + if inst.Labels["app.kubernetes.io/managed-by"] == "" { + inst.Labels["app.kubernetes.io/managed-by"] = "k8s-agents-operator" + } + if inst.Spec.LicenseKeySecret == "" { + inst.Spec.LicenseKeySecret = "newrelic-key-secret" + } + return nil +} + +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. //+kubebuilder:webhook:verbs=create;update,path=/validate-newrelic-com-v1alpha2-instrumentation,mutating=false,failurePolicy=fail,groups=newrelic.com,resources=instrumentations,versions=v1alpha2,name=vinstrumentationcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1 //+kubebuilder:webhook:verbs=delete,path=/validate-newrelic-com-v1alpha2-instrumentation,mutating=false,failurePolicy=ignore,groups=newrelic.com,resources=instrumentations,versions=v1alpha2,name=vinstrumentationdelete.kb.io,sideEffects=none,admissionReviewVersions=v1 + +const ( + envNewRelicPrefix = "NEW_RELIC_" + envOtelPrefix = "OTEL_" +) + +var validEnvPrefixes = []string{envNewRelicPrefix, envOtelPrefix} +var validEnvPrefixesStr = strings.Join(validEnvPrefixes, ", ") + +var _ webhook.CustomValidator = &Instrumentation{} + +// ValidateCreate to validate the creation operation +func (r *Instrumentation) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + inst := obj.(*Instrumentation) + instrumentationLog.Info("validate create", "name", inst.Name) + return r.validate(inst) +} + +// ValidateUpdate to validate the update operation +func (r *Instrumentation) ValidateUpdate(ctx context.Context, oldObj runtime.Object, newObj runtime.Object) (admission.Warnings, error) { + inst := newObj.(*Instrumentation) + instrumentationLog.Info("validate update", "name", inst.Name) + return r.validate(inst) +} + +// ValidateDelete to validate the deletion operation +func (r *Instrumentation) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + inst := obj.(*Instrumentation) + instrumentationLog.Info("validate delete", "name", inst.Name) + return r.validate(inst) +} + +// validate to validate all the fields +func (r *Instrumentation) validate(inst *Instrumentation) (admission.Warnings, error) { + // TODO: Maybe improve this + acceptableLangs := []string{"dotnet", "go", "java", "nodejs", "php-7.2", "php-7.3", "php-7.4", "php-8.0", "php-8.1", "php-8.2", "php-8.3", "python", "ruby"} + agentLang := inst.Spec.Agent.Language + if !slices.Contains(acceptableLangs, agentLang) { + return nil, fmt.Errorf("instrumentation agent language %q must be one of the accepted languages (%s)", agentLang, strings.Join(acceptableLangs, ", ")) + } + + if err := r.validateEnv(inst.Spec.Agent.Env); err != nil { + return nil, err + } + + if inst.Spec.Agent.IsEmpty() { + return nil, fmt.Errorf("instrumentation %q agent is empty", inst.Name) + } + + if _, err := metav1.LabelSelectorAsSelector(&inst.Spec.PodLabelSelector); err != nil { + return nil, err + } + if _, err := metav1.LabelSelectorAsSelector(&inst.Spec.NamespaceLabelSelector); err != nil { + return nil, err + } + + return nil, nil +} + +// validateEnv to validate the environment variables used all start with the required prefixes +func (r *Instrumentation) validateEnv(envs []corev1.EnvVar) error { + var invalidNames []string + for _, env := range envs { + var valid bool + for _, validEnvPrefix := range validEnvPrefixes { + if strings.HasPrefix(env.Name, validEnvPrefix) { + valid = true + break + } + } + if !valid { + invalidNames = append(invalidNames, env.Name) + } + } + if len(invalidNames) > 0 { + return fmt.Errorf("env name should start with %s; found these invalid names %s", validEnvPrefixesStr, strings.Join(invalidNames, ", ")) + } + return nil +} diff --git a/src/api/v1alpha2/zz_generated.deepcopy.go b/src/api/v1alpha2/zz_generated.deepcopy.go index 0b7dc244..7d96769a 100644 --- a/src/api/v1alpha2/zz_generated.deepcopy.go +++ b/src/api/v1alpha2/zz_generated.deepcopy.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha2 import ( - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) diff --git a/src/instrumentation/instrumentation_defaulter.go b/src/instrumentation/instrumentation_defaulter.go deleted file mode 100644 index d5205ff3..00000000 --- a/src/instrumentation/instrumentation_defaulter.go +++ /dev/null @@ -1,34 +0,0 @@ -package instrumentation - -import ( - "context" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" -) - -var _ webhook.CustomDefaulter = (*InstrumentationDefaulter)(nil) - -// InstrumentationDefaulter is used to set defaults for instrumentation -type InstrumentationDefaulter struct { - Logger logr.Logger -} - -// Default to set the default values for Instrumentation -func (r *InstrumentationDefaulter) Default(ctx context.Context, obj runtime.Object) error { - inst := obj.(*v1alpha2.Instrumentation) - r.Logger.V(1).Info("default", "name", inst.Name) - if inst.Labels == nil { - inst.Labels = map[string]string{} - } - if inst.Labels["app.kubernetes.io/managed-by"] == "" { - inst.Labels["app.kubernetes.io/managed-by"] = "k8s-agents-operator" - } - if inst.Spec.LicenseKeySecret == "" { - inst.Spec.LicenseKeySecret = "newrelic-key-secret" - } - return nil -} diff --git a/src/instrumentation/instrumentation_validator.go b/src/instrumentation/instrumentation_validator.go deleted file mode 100644 index e1f9c571..00000000 --- a/src/instrumentation/instrumentation_validator.go +++ /dev/null @@ -1,102 +0,0 @@ -package instrumentation - -import ( - "context" - "fmt" - "slices" - "strings" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" -) - -const ( - envNewRelicPrefix = "NEW_RELIC_" - envOtelPrefix = "OTEL_" -) - -var validEnvPrefixes = []string{envNewRelicPrefix, envOtelPrefix} -var validEnvPrefixesStr = strings.Join(validEnvPrefixes, ", ") - -var _ webhook.CustomValidator = (*InstrumentationValidator)(nil) - -// InstrumentationValidator is used to validate instrumentations -type InstrumentationValidator struct { - Logger logr.Logger - InjectorRegistery *apm.InjectorRegistery -} - -// ValidateCreate to validate the creation operation -func (r *InstrumentationValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - inst := obj.(*v1alpha2.Instrumentation) - r.Logger.V(1).Info("validate_create", "name", inst.Name) - return r.validate(inst) -} - -// ValidateUpdate to validate the update operation -func (r *InstrumentationValidator) ValidateUpdate(ctx context.Context, oldObj runtime.Object, newObj runtime.Object) (admission.Warnings, error) { - inst := newObj.(*v1alpha2.Instrumentation) - r.Logger.V(1).Info("validate_update", "name", inst.Name) - return r.validate(inst) -} - -// ValidateDelete to validate the deletion operation -func (r *InstrumentationValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - inst := obj.(*v1alpha2.Instrumentation) - r.Logger.V(1).Info("validate_delete", "name", inst.Name) - return r.validate(inst) -} - -// validate to validate all the fields -func (r *InstrumentationValidator) validate(inst *v1alpha2.Instrumentation) (admission.Warnings, error) { - acceptableLangs := r.InjectorRegistery.GetInjectors().Names() - agentLang := inst.Spec.Agent.Language - if !slices.Contains(acceptableLangs, agentLang) { - return nil, fmt.Errorf("instrumentation agent language %q must be one of the accepted languages (%s)", agentLang, strings.Join(acceptableLangs, ", ")) - } - - if err := r.validateEnv(inst.Spec.Agent.Env); err != nil { - return nil, err - } - - if inst.Spec.Agent.IsEmpty() { - return nil, fmt.Errorf("instrumentation %q agent is empty", inst.Name) - } - - if _, err := metav1.LabelSelectorAsSelector(&inst.Spec.PodLabelSelector); err != nil { - return nil, err - } - if _, err := metav1.LabelSelectorAsSelector(&inst.Spec.NamespaceLabelSelector); err != nil { - return nil, err - } - - return nil, nil -} - -// validateEnv to validate the environment variables used all start with the required prefixes -func (r *InstrumentationValidator) validateEnv(envs []corev1.EnvVar) error { - var invalidNames []string - for _, env := range envs { - var valid bool - for _, validEnvPrefix := range validEnvPrefixes { - if strings.HasPrefix(env.Name, validEnvPrefix) { - valid = true - break - } - } - if !valid { - invalidNames = append(invalidNames, env.Name) - } - } - if len(invalidNames) > 0 { - return fmt.Errorf("env name should start with %s; found these invalid names %s", validEnvPrefixesStr, strings.Join(invalidNames, ", ")) - } - return nil -} diff --git a/src/apm/dotnet.go b/src/internal/apm/dotnet.go similarity index 100% rename from src/apm/dotnet.go rename to src/internal/apm/dotnet.go diff --git a/src/apm/dotnet_test.go b/src/internal/apm/dotnet_test.go similarity index 100% rename from src/apm/dotnet_test.go rename to src/internal/apm/dotnet_test.go diff --git a/src/apm/golang.go b/src/internal/apm/golang.go similarity index 100% rename from src/apm/golang.go rename to src/internal/apm/golang.go diff --git a/src/apm/golang_test.go b/src/internal/apm/golang_test.go similarity index 100% rename from src/apm/golang_test.go rename to src/internal/apm/golang_test.go diff --git a/src/apm/helper.go b/src/internal/apm/helper.go similarity index 100% rename from src/apm/helper.go rename to src/internal/apm/helper.go diff --git a/src/apm/helper_test.go b/src/internal/apm/helper_test.go similarity index 100% rename from src/apm/helper_test.go rename to src/internal/apm/helper_test.go diff --git a/src/apm/java.go b/src/internal/apm/java.go similarity index 100% rename from src/apm/java.go rename to src/internal/apm/java.go diff --git a/src/apm/java_test.go b/src/internal/apm/java_test.go similarity index 100% rename from src/apm/java_test.go rename to src/internal/apm/java_test.go diff --git a/src/apm/nodejs.go b/src/internal/apm/nodejs.go similarity index 100% rename from src/apm/nodejs.go rename to src/internal/apm/nodejs.go diff --git a/src/apm/nodejs_test.go b/src/internal/apm/nodejs_test.go similarity index 100% rename from src/apm/nodejs_test.go rename to src/internal/apm/nodejs_test.go diff --git a/src/apm/php.go b/src/internal/apm/php.go similarity index 100% rename from src/apm/php.go rename to src/internal/apm/php.go diff --git a/src/apm/php_test.go b/src/internal/apm/php_test.go similarity index 100% rename from src/apm/php_test.go rename to src/internal/apm/php_test.go diff --git a/src/apm/python.go b/src/internal/apm/python.go similarity index 100% rename from src/apm/python.go rename to src/internal/apm/python.go diff --git a/src/apm/python_test.go b/src/internal/apm/python_test.go similarity index 100% rename from src/apm/python_test.go rename to src/internal/apm/python_test.go diff --git a/src/apm/ruby.go b/src/internal/apm/ruby.go similarity index 100% rename from src/apm/ruby.go rename to src/internal/apm/ruby.go diff --git a/src/apm/ruby_test.go b/src/internal/apm/ruby_test.go similarity index 100% rename from src/apm/ruby_test.go rename to src/internal/apm/ruby_test.go diff --git a/src/autodetect/main.go b/src/internal/autodetect/main.go similarity index 100% rename from src/autodetect/main.go rename to src/internal/autodetect/main.go diff --git a/src/autodetect/main_test.go b/src/internal/autodetect/main_test.go similarity index 97% rename from src/autodetect/main_test.go rename to src/internal/autodetect/main_test.go index 6323cf69..461359a6 100644 --- a/src/autodetect/main_test.go +++ b/src/internal/autodetect/main_test.go @@ -18,6 +18,7 @@ package autodetect_test import ( "encoding/json" + "github.com/newrelic/k8s-agents-operator/src/internal/autodetect" "net/http" "net/http/httptest" "testing" @@ -26,8 +27,6 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" - - "github.com/newrelic/k8s-agents-operator/src/autodetect" ) func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) { diff --git a/src/autodetect/openshiftroutes.go b/src/internal/autodetect/openshiftroutes.go similarity index 100% rename from src/autodetect/openshiftroutes.go rename to src/internal/autodetect/openshiftroutes.go diff --git a/src/internal/config/main.go b/src/internal/config/main.go index c61988d9..98302180 100644 --- a/src/internal/config/main.go +++ b/src/internal/config/main.go @@ -17,13 +17,13 @@ limitations under the License. package config import ( + "github.com/newrelic/k8s-agents-operator/src/internal/autodetect" "sync" "time" "github.com/go-logr/logr" logf "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/newrelic/k8s-agents-operator/src/autodetect" "github.com/newrelic/k8s-agents-operator/src/internal/version" ) diff --git a/src/internal/config/main_test.go b/src/internal/config/main_test.go index 388a08a6..1b409172 100644 --- a/src/internal/config/main_test.go +++ b/src/internal/config/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package config_test import ( + "github.com/newrelic/k8s-agents-operator/src/internal/autodetect" "sync/atomic" "testing" "time" @@ -24,7 +25,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/newrelic/k8s-agents-operator/src/autodetect" "github.com/newrelic/k8s-agents-operator/src/internal/config" ) diff --git a/src/internal/config/options.go b/src/internal/config/options.go index d1d2aa77..d72b4c78 100644 --- a/src/internal/config/options.go +++ b/src/internal/config/options.go @@ -17,13 +17,11 @@ limitations under the License. package config import ( - "regexp" - "strings" + "github.com/newrelic/k8s-agents-operator/src/internal/autodetect" "time" "github.com/go-logr/logr" - "github.com/newrelic/k8s-agents-operator/src/autodetect" "github.com/newrelic/k8s-agents-operator/src/internal/version" ) @@ -74,28 +72,3 @@ func WithVersion(v version.Version) Option { o.version = v } } - -func WithLabelFilters(labelFilters []string) Option { - return func(o *options) { - - filters := []string{} - for _, pattern := range labelFilters { - var result strings.Builder - - for i, literal := range strings.Split(pattern, "*") { - - // Replace * with .* - if i > 0 { - result.WriteString(".*") - } - - // Quote any regular expression meta characters in the - // literal text. - result.WriteString(regexp.QuoteMeta(literal)) - } - filters = append(filters, result.String()) - } - - o.labelsFilter = filters - } -} diff --git a/src/internal/controller/instrumentation_controller.go b/src/internal/controller/instrumentation_controller.go new file mode 100644 index 00000000..91d766ad --- /dev/null +++ b/src/internal/controller/instrumentation_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2024. + +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 controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" +) + +// InstrumentationReconciler reconciles a Instrumentation object +type InstrumentationReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=newrelic.com,resources=instrumentations,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=newrelic.com,resources=instrumentations/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=newrelic.com,resources=instrumentations/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Instrumentation object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/reconcile +func (r *InstrumentationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *InstrumentationReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha2.Instrumentation{}). + Complete(r) +} diff --git a/src/instrumentation/instrumentation_suite_test.go b/src/internal/instrumentation/instrumentation_suite_test.go similarity index 91% rename from src/instrumentation/instrumentation_suite_test.go rename to src/internal/instrumentation/instrumentation_suite_test.go index 7340164a..3763d5ad 100644 --- a/src/instrumentation/instrumentation_suite_test.go +++ b/src/internal/instrumentation/instrumentation_suite_test.go @@ -17,7 +17,7 @@ var k8sClient client.Client func TestMain(m *testing.M) { testEnv := &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, } cfg, err := testEnv.Start() diff --git a/src/instrumentation/podmutator.go b/src/internal/instrumentation/mutator.go similarity index 95% rename from src/instrumentation/podmutator.go rename to src/internal/instrumentation/mutator.go index 19d056c9..9163c110 100644 --- a/src/instrumentation/podmutator.go +++ b/src/internal/instrumentation/mutator.go @@ -26,15 +26,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/internal/webhookhandler" ) // compile time type assertion var ( - _ webhookhandler.PodMutator = (*instPodMutator)(nil) - _ InstrumentationLocator = (*NewrelicInstrumentationLocator)(nil) - _ SdkInjector = (*NewrelicSdkInjector)(nil) - _ SecretReplicator = (*NewrelicSecretReplicator)(nil) + _ InstrumentationLocator = (*NewrelicInstrumentationLocator)(nil) + _ SdkInjector = (*NewrelicSdkInjector)(nil) + _ SecretReplicator = (*NewrelicSecretReplicator)(nil) ) var ( @@ -42,7 +40,7 @@ var ( errNoInstancesAvailable = errors.New("no New Relic Instrumentation instances available") ) -type instPodMutator struct { +type InstrumentationPodMutator struct { logger logr.Logger client client.Client sdkInjector SdkInjector @@ -59,8 +57,8 @@ func NewMutator( secretReplicator SecretReplicator, instrumentationLocator InstrumentationLocator, operatorNamespace string, -) *instPodMutator { - return &instPodMutator{ +) *InstrumentationPodMutator { + return &InstrumentationPodMutator{ logger: logger, client: client, sdkInjector: sdkInjector, @@ -71,7 +69,7 @@ func NewMutator( } // Mutate is used to mutate a pod based on some instrumentation(s) -func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod corev1.Pod) (corev1.Pod, error) { +func (pm *InstrumentationPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod corev1.Pod) (corev1.Pod, error) { logger := pm.logger.WithValues("namespace", pod.Namespace, "name", pod.Name, "generate_name", pod.GenerateName) instCandidates, err := pm.instrumentationLocator.GetInstrumentations(ctx, ns, pod) diff --git a/src/instrumentation/podmutator_test.go b/src/internal/instrumentation/mutator_test.go similarity index 99% rename from src/instrumentation/podmutator_test.go rename to src/internal/instrumentation/mutator_test.go index c47c038b..59ecf1ee 100644 --- a/src/instrumentation/podmutator_test.go +++ b/src/internal/instrumentation/mutator_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "github.com/newrelic/k8s-agents-operator/src/internal/apm" "testing" "github.com/go-logr/logr" @@ -17,7 +18,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" ) type FakeInjector func(ctx context.Context, insts []*v1alpha2.Instrumentation, ns corev1.Namespace, pod corev1.Pod) corev1.Pod diff --git a/src/instrumentation/sdk.go b/src/internal/instrumentation/sdk.go similarity index 98% rename from src/instrumentation/sdk.go rename to src/internal/instrumentation/sdk.go index f66a250b..17f78773 100644 --- a/src/instrumentation/sdk.go +++ b/src/internal/instrumentation/sdk.go @@ -19,7 +19,7 @@ package instrumentation import ( "context" "fmt" - + "github.com/newrelic/k8s-agents-operator/src/internal/apm" "runtime/debug" "github.com/go-logr/logr" @@ -27,7 +27,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" ) const ( diff --git a/src/instrumentation/sdk_test.go b/src/internal/instrumentation/sdk_test.go similarity index 98% rename from src/instrumentation/sdk_test.go rename to src/internal/instrumentation/sdk_test.go index d0eabfb6..2424e803 100644 --- a/src/instrumentation/sdk_test.go +++ b/src/internal/instrumentation/sdk_test.go @@ -3,6 +3,7 @@ package instrumentation import ( "context" "fmt" + "github.com/newrelic/k8s-agents-operator/src/internal/apm" "testing" "github.com/go-logr/logr" @@ -12,7 +13,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" ) var _ apm.Injector = (*ErrorInjector)(nil) @@ -172,7 +172,7 @@ func TestNewrelicSdkInjector_Inject(t *testing.T) { for _, apmInjector := range apmInjectors { injectorRegistry.MustRegister(apmInjector) } - defaulter := InstrumentationDefaulter{Logger: logger} + defaulter := v1alpha2.Instrumentation{} for _, langInst := range test.langInsts { _ = defaulter.Default(ctx, langInst) } diff --git a/src/instrumentation/upgrade/upgrade.go b/src/internal/migrate/upgrade/upgrade.go similarity index 100% rename from src/instrumentation/upgrade/upgrade.go rename to src/internal/migrate/upgrade/upgrade.go diff --git a/src/instrumentation/upgrade/upgrade_suite_test.go b/src/internal/migrate/upgrade/upgrade_suite_test.go similarity index 95% rename from src/instrumentation/upgrade/upgrade_suite_test.go rename to src/internal/migrate/upgrade/upgrade_suite_test.go index 0ce96251..bb5b98fe 100644 --- a/src/instrumentation/upgrade/upgrade_suite_test.go +++ b/src/internal/migrate/upgrade/upgrade_suite_test.go @@ -38,7 +38,7 @@ var cfg *rest.Config func TestMain(m *testing.M) { testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "config", "crd", "bases"), + filepath.Join("..", "..", "..", "..", "config", "crd", "bases"), }, } diff --git a/src/instrumentation/upgrade/upgrade_test.go b/src/internal/migrate/upgrade/upgrade_test.go similarity index 94% rename from src/instrumentation/upgrade/upgrade_test.go rename to src/internal/migrate/upgrade/upgrade_test.go index 54c2fc9c..f768d707 100644 --- a/src/instrumentation/upgrade/upgrade_test.go +++ b/src/internal/migrate/upgrade/upgrade_test.go @@ -17,7 +17,6 @@ package upgrade import ( "context" - "github.com/newrelic/k8s-agents-operator/src/instrumentation" "strings" "testing" @@ -31,7 +30,6 @@ import ( ) func TestUpgrade(t *testing.T) { - logger := logr.Discard() ctx := context.Background() nsName := strings.ToLower(t.Name()) err := k8sClient.Create(context.Background(), &corev1.Namespace{ @@ -47,7 +45,7 @@ func TestUpgrade(t *testing.T) { Namespace: nsName, }, } - defaulter := instrumentation.InstrumentationDefaulter{Logger: logger} + defaulter := v1alpha2.Instrumentation{} _ = defaulter.Default(ctx, inst) err = k8sClient.Create(context.Background(), inst) require.NoError(t, err) diff --git a/src/internal/webhookhandler/webhookhandler.go b/src/internal/webhook/podmutationhandler.go similarity index 55% rename from src/internal/webhookhandler/webhookhandler.go rename to src/internal/webhook/podmutationhandler.go index 5c6a2dc2..6694ae02 100644 --- a/src/internal/webhookhandler/webhookhandler.go +++ b/src/internal/webhook/podmutationhandler.go @@ -1,34 +1,24 @@ -/* -Copyright 2024. - -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 webhookhandler contains the webhook that injects sidecars into pods. -package webhookhandler +package webhook import ( "context" "encoding/json" - "net/http" - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" + "github.com/newrelic/k8s-agents-operator/src/internal/apm" + "github.com/newrelic/k8s-agents-operator/src/internal/instrumentation" "k8s.io/apimachinery/pkg/types" + "net/http" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "github.com/newrelic/k8s-agents-operator/src/internal/config" + corev1 "k8s.io/api/core/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +// compile time type assertion +var ( + _ PodMutator = (*instrumentation.InstrumentationPodMutator)(nil) ) // +kubebuilder:webhook:path=/mutate-v1-pod,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create;update,versions=v1,name=mpod.kb.io,sideEffects=none,admissionReviewVersions=v1 @@ -36,24 +26,15 @@ import ( // +kubebuilder:rbac:groups=newrelic.com,resources=instrumentations,verbs=get;list;watch // +kubebuilder:rbac:groups="apps",resources=replicasets,verbs=get;list;watch // +kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;create;delete;deletecollection;patch;update;watch -var _ WebhookHandler = (*podMutationHandler)(nil) - -// WebhookHandler is a webhook handler that analyzes new pods and injects appropriate sidecars into it. -type WebhookHandler interface { - admission.Handler -} - -// the implementation. -type podMutationHandler struct { - client client.Client - decoder admission.Decoder - logger logr.Logger - podMutators []PodMutator - config config.Config +// PodMutationHandler is a webhook handler for mutating Pods +type PodMutationHandler struct { + Client client.Client + Decoder admission.Decoder + Mutators []PodMutator + Logger logr.Logger } // PodMutator mutates a pod. @@ -61,27 +42,21 @@ type PodMutator interface { Mutate(ctx context.Context, ns corev1.Namespace, pod corev1.Pod) (corev1.Pod, error) } -// NewWebhookHandler creates a new WebhookHandler. -func NewWebhookHandler(cfg config.Config, logger logr.Logger, cl client.Client, decoder admission.Decoder, podMutators []PodMutator) WebhookHandler { - return &podMutationHandler{ - config: cfg, - logger: logger, - client: cl, - podMutators: podMutators, - decoder: decoder, - } -} +//var podMutatorLog = ctrl.Log.WithName("pod-mutator") -func (p *podMutationHandler) Handle(ctx context.Context, req admission.Request) admission.Response { +// Handle manages Pod mutations +func (m *PodMutationHandler) Handle(ctx context.Context, req admission.Request) admission.Response { pod := corev1.Pod{} - err := p.decoder.Decode(req, &pod) + err := m.Decoder.Decode(req, &pod) if err != nil { return admission.Errored(http.StatusBadRequest, err) } + m.Logger.Info("Mutating Pod", "name", pod.Name) + // we use the req.Namespace here because the pod might have not been created yet ns := corev1.Namespace{} - err = p.client.Get(ctx, types.NamespacedName{Name: req.Namespace, Namespace: ""}, &ns) + err = m.Client.Get(ctx, types.NamespacedName{Name: req.Namespace, Namespace: ""}, &ns) if err != nil { res := admission.Errored(http.StatusInternalServerError, err) // By default, admission.Errored sets Allowed to false which blocks pod creation even though the failurePolicy=ignore. @@ -93,8 +68,8 @@ func (p *podMutationHandler) Handle(ctx context.Context, req admission.Request) return res } - for _, m := range p.podMutators { - pod, err = m.Mutate(ctx, ns, pod) + for _, mutator := range m.Mutators { + pod, err = mutator.Mutate(ctx, ns, pod) if err != nil { //@todo: actually print the error message res := admission.Errored(http.StatusInternalServerError, err) @@ -105,9 +80,40 @@ func (p *podMutationHandler) Handle(ctx context.Context, req admission.Request) marshaledPod, err := json.Marshal(pod) if err != nil { + m.Logger.Error(err, "failed to marshal pod") res := admission.Errored(http.StatusInternalServerError, err) res.Allowed = true return res } + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod) } + +// SetupWebhookWithManager registers the pod mutation webhook +func SetupWebhookWithManager(mgr ctrl.Manager, operatorNamespace string, logger logr.Logger) error { + // Setup InstrumentationMutator + mgrClient := mgr.GetClient() + injectorRegistry := apm.DefaultInjectorRegistry + injector := instrumentation.NewNewrelicSdkInjector(logger, mgrClient, injectorRegistry) + secretReplicator := instrumentation.NewNewrelicSecretReplicator(logger, mgrClient) + instrumentationLocator := instrumentation.NewNewRelicInstrumentationLocator(logger, mgrClient, operatorNamespace) + + hookServer := mgr.GetWebhookServer() + hookServer.Register("/mutate-v1-pod", &webhook.Admission{Handler: &PodMutationHandler{ + Client: mgr.GetClient(), + Decoder: admission.NewDecoder(mgr.GetScheme()), + Mutators: []PodMutator{ + instrumentation.NewMutator( + logger, + mgrClient, + injector, + secretReplicator, + instrumentationLocator, + operatorNamespace, + ), + }, + Logger: logger, + }}) + + return nil +} diff --git a/src/internal/webhookhandler/webhookhandler_suite_test.go b/src/internal/webhook/podmutationhandler_suite_test.go similarity index 86% rename from src/internal/webhookhandler/webhookhandler_suite_test.go rename to src/internal/webhook/podmutationhandler_suite_test.go index d3f30feb..9397732b 100644 --- a/src/internal/webhookhandler/webhookhandler_suite_test.go +++ b/src/internal/webhook/podmutationhandler_suite_test.go @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package webhookhandler_test +package webhook_test import ( "context" "crypto/tls" "fmt" + "github.com/newrelic/k8s-agents-operator/src/internal/apm" + "github.com/newrelic/k8s-agents-operator/src/internal/instrumentation" + "github.com/newrelic/k8s-agents-operator/src/internal/webhook" "io" "net" "os" @@ -33,6 +36,8 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" apierrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" + "github.com/newrelic/k8s-agents-operator/src/internal/version" admissionv1 "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -46,16 +51,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" - "github.com/newrelic/k8s-agents-operator/src/autodetect" - "github.com/newrelic/k8s-agents-operator/src/instrumentation" - "github.com/newrelic/k8s-agents-operator/src/internal/config" - "github.com/newrelic/k8s-agents-operator/src/internal/version" - "github.com/newrelic/k8s-agents-operator/src/internal/webhookhandler" + webhookruntime "sigs.k8s.io/controller-runtime/pkg/webhook" // +kubebuilder:scaffold:imports ) @@ -128,7 +124,7 @@ func TestMain(m *testing.M) { webhookInstallOptions := &testEnv.WebhookInstallOptions mgr, mgrErr := ctrl.NewManager(cfg, ctrl.Options{ Scheme: testScheme, - WebhookServer: webhook.NewServer(webhook.Options{ + WebhookServer: webhookruntime.NewServer(webhookruntime.Options{ Host: webhookInstallOptions.LocalServingHost, Port: webhookInstallOptions.LocalServingPort, CertDir: webhookInstallOptions.LocalServingCertDir, @@ -141,59 +137,16 @@ func TestMain(m *testing.M) { os.Exit(1) } - injectorRegistry := apm.DefaultInjectorRegistry - - instDefaulter := &instrumentation.InstrumentationDefaulter{ - Logger: logger.WithName("instrumentation-defaulter"), - } - instValidator := &instrumentation.InstrumentationValidator{ - Logger: logger.WithName("instrumentation-validator"), - InjectorRegistery: injectorRegistry, - } - err = ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha2.Instrumentation{}). - WithValidator(instValidator). - WithDefaulter(instDefaulter). - Complete() - if err != nil { - fmt.Printf("failed to register instrumentation webhook: %v", mgrErr) + if err = (&v1alpha2.Instrumentation{}).SetupWebhookWithManager(mgr, logger); err != nil { + logger.Error(err, "unable to create webhook", "webhook", "Instrumentation") os.Exit(1) } - restConfig := cfg operatorNamespace := "newrelic" - var labelsFilter []string - ad, err := autodetect.New(restConfig) - if err != nil { - fmt.Printf("failed to setup auto-detect routine: %v", err) + if err = webhook.SetupWebhookWithManager(mgr, operatorNamespace, logger); err != nil { + logger.Error(err, "unable to register pod mutate webhook") os.Exit(1) } - v := version.Get() - hcfg := config.New( - config.WithLogger(ctrl.Log.WithName("config")), - config.WithVersion(v), - config.WithAutoDetect(ad), - config.WithLabelFilters(labelsFilter), - ) - client := mgr.GetClient() - injector := instrumentation.NewNewrelicSdkInjector(logger, client, injectorRegistry) - secretReplicator := instrumentation.NewNewrelicSecretReplicator(logger, client) - instrumentationLocator := instrumentation.NewNewRelicInstrumentationLocator(logger, client, operatorNamespace) - mgr.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{ - Handler: webhookhandler.NewWebhookHandler( - hcfg, ctrl.Log.WithName("pod-webhook"), mgr.GetClient(), admission.NewDecoder(mgr.GetScheme()), - []webhookhandler.PodMutator{ - instrumentation.NewMutator( - logger, - client, - injector, - secretReplicator, - instrumentationLocator, - operatorNamespace, - ), - }, - ), - }) go func() { if err = mgr.Start(ctx); err != nil { diff --git a/src/main.go b/src/main.go index 02e3d8db..4fce1cfa 100644 --- a/src/main.go +++ b/src/main.go @@ -21,36 +21,31 @@ import ( "crypto/tls" "flag" "fmt" + "github.com/newrelic/k8s-agents-operator/src/internal/autodetect" + instrumentationupgrade "github.com/newrelic/k8s-agents-operator/src/internal/migrate/upgrade" + "github.com/newrelic/k8s-agents-operator/src/internal/webhook" "os" "runtime" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" "strings" "time" + "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" + "github.com/newrelic/k8s-agents-operator/src/internal/config" + "github.com/newrelic/k8s-agents-operator/src/internal/version" routev1 "github.com/openshift/api/route/v1" - "github.com/spf13/pflag" + k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - k8sapiflag "k8s.io/component-base/cli/flag" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/newrelic/k8s-agents-operator/src/api/v1alpha2" - "github.com/newrelic/k8s-agents-operator/src/apm" - "github.com/newrelic/k8s-agents-operator/src/autodetect" - "github.com/newrelic/k8s-agents-operator/src/instrumentation" - instrumentationupgrade "github.com/newrelic/k8s-agents-operator/src/instrumentation/upgrade" - "github.com/newrelic/k8s-agents-operator/src/internal/config" - "github.com/newrelic/k8s-agents-operator/src/internal/version" - "github.com/newrelic/k8s-agents-operator/src/internal/webhookhandler" - + webhookruntime "sigs.k8s.io/controller-runtime/pkg/webhook" // +kubebuilder:scaffold:imports ) @@ -68,50 +63,49 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(v1alpha2.AddToScheme(scheme)) - utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) // TODO: Update this to not use a deprecated method // +kubebuilder:scaffold:scheme } func main() { - // registers any flags that underlying libraries might use - opts := zap.Options{} - opts.BindFlags(flag.CommandLine) - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - - v := version.Get() - - // add flags related to this operator - var ( - metricsAddr string - probeAddr string - enableLeaderElection bool - labelsFilter []string - webhookPort int - tlsOpt tlsConfig - secureMetrics bool - enableHTTP2 bool - ) - - pflag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - pflag.StringVar(&probeAddr, "health-probe-addr", ":8081", "The address the probe endpoint binds to.") - pflag.BoolVar(&enableLeaderElection, "enable-leader-election", false, + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "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.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the\tflag.BoolVar(&enableHTTP2, \"enable-http2\", false,\n\t\t\"If set, HTTP/2 will be enabled for the metrics and webhook servers\")\n metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + opts := zap.Options{} + opts.BindFlags(flag.CommandLine) + flag.Parse() - pflag.StringArrayVar(&labelsFilter, "labels", []string{}, "Labels to filter away from propagating onto deploys") - pflag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook endpoint binds to.") - pflag.StringVar(&tlsOpt.minVersion, "tls-min-version", "VersionTLS12", "Minimum TLS version supported. Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants.") - pflag.StringSliceVar(&tlsOpt.cipherSuites, "tls-cipher-suites", nil, "Comma-separated list of cipher suites for the server. Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). If omitted, the default Go cipher suites will be used") + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - flag.BoolVar(&secureMetrics, "metrics-secure", false, - "If set the metrics endpoint is served securely") - flag.BoolVar(&enableHTTP2, "enable-http2", false, - "If set, HTTP/2 will be enabled for the metrics and webhook servers") + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } - pflag.Parse() + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } - logger := zap.New(zap.UseFlagOptions(&opts)) - ctrl.SetLogger(logger) + webhookServer := webhookruntime.NewServer(webhookruntime.Options{ + TLSOpts: tlsOpts, + }) operatorNamespace := os.Getenv("OPERATOR_NAMESPACE") if operatorNamespace == "" { @@ -119,18 +113,18 @@ func main() { os.Exit(1) } - logger.Info("Starting the Kubernetes Agents Operator", + v := version.Get() + + setupLog.Info("Starting the Kubernetes Agents Operator", "k8s-agents-operator", v.Operator, "running-namespace", operatorNamespace, "build-date", v.BuildDate, "go-version", v.Go, "go-arch", runtime.GOARCH, "go-os", runtime.GOOS, - "labels-filter", labelsFilter, ) - logger.Info("Working!") - + // TODO: Start determine usage restConfig := ctrl.GetConfigOrDie() // builds the operator's configuration @@ -144,14 +138,31 @@ func main() { config.WithLogger(ctrl.Log.WithName("config")), config.WithVersion(v), config.WithAutoDetect(ad), - config.WithLabelFilters(labelsFilter), ) + // End determine usage + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + // TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are + // not provided, self-signed certificates will be generated by default. This option is not recommended for + // production environments as self-signed certificates do not offer the same level of trust and security + // as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing + // unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName + // to provide certificates, ensuring the server communicates using trusted and secure certificates. + TLSOpts: tlsOpts, + } - watchNamespace, found := os.LookupEnv("WATCH_NAMESPACE") - if found { - setupLog.Info("watching namespace(s)", "namespaces", watchNamespace) - } else { - setupLog.Info("the env var WATCH_NAMESPACE isn't set, watching all namespaces") + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization } // see https://github.com/openshift/library-go/blob/4362aa519714a4b62b00ab8318197ba2bba51cb7/pkg/config/leaderelection/leaderelection.go#L104 @@ -159,41 +170,34 @@ func main() { renewDeadline := time.Second * 107 retryPeriod := time.Second * 26 - // if the enable-http2 flag is false (the default), http/2 should be disabled - // due to its vulnerabilities. More specifically, disabling http/2 will - // prevent from being vulnerable to the HTTP/2 Stream Cancellation and - // Rapid Reset CVEs. For more information see: - // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 - // - https://github.com/advisories/GHSA-4374-p667-p6c8 - disableHTTP2 := func(c *tls.Config) { - setupLog.Info("disabling http/2") - c.NextProtos = []string{"http/1.1"} - } - - tlsOpts := []func(*tls.Config){} - if !enableHTTP2 { - tlsOpts = append(tlsOpts, disableHTTP2) - } - - webhookServer := webhook.NewServer(webhook.Options{ - TLSOpts: tlsOpts, - Port: webhookPort, - }) - mgrOptions := ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ - BindAddress: metricsAddr, - SecureServing: secureMetrics, - TLSOpts: tlsOpts, - }, + Scheme: scheme, + Metrics: metricsServerOptions, WebhookServer: webhookServer, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "9f7554c3.newrelic.com", - LeaseDuration: &leaseDuration, - RenewDeadline: &renewDeadline, - RetryPeriod: &retryPeriod, + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + LeaseDuration: &leaseDuration, + RenewDeadline: &renewDeadline, + RetryPeriod: &retryPeriod, + } + + watchNamespace, found := os.LookupEnv("WATCH_NAMESPACE") + if found { + setupLog.Info("watching namespace(s)", "namespaces", watchNamespace) + } else { + setupLog.Info("the env var WATCH_NAMESPACE isn't set, watching all namespaces") } if watchNamespace != "" { @@ -204,12 +208,13 @@ func main() { mgrOptions.Cache = cache.Options{DefaultNamespaces: nsDefaults} } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mgrOptions) + mgr, err := ctrl.NewManager(restConfig, mgrOptions) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } + // TODO: Use controller paradigm & investigate below ctx := ctrl.SetupSignalHandler() err = addDependencies(ctx, mgr, cfg) if err != nil { @@ -218,45 +223,16 @@ func main() { } if os.Getenv("ENABLE_WEBHOOKS") != "false" { - //configure injectors that we accept. go, dotnet, etc.. - injectorRegistry := apm.DefaultInjectorRegistry - - instDefaulter := &instrumentation.InstrumentationDefaulter{ - Logger: logger.WithName("instrumentation-defaulter"), - } - instValidator := &instrumentation.InstrumentationValidator{ - Logger: logger.WithName("instrumentation-validator"), - InjectorRegistery: injectorRegistry, - } - err = ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha2.Instrumentation{}). - WithValidator(instValidator). - WithDefaulter(instDefaulter). - Complete() - if err != nil { + if err = (&v1alpha2.Instrumentation{}).SetupWebhookWithManager(mgr, ctrl.Log.WithName("instrumentation-validator")); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Instrumentation") os.Exit(1) } - client := mgr.GetClient() - injector := instrumentation.NewNewrelicSdkInjector(logger, client, injectorRegistry) - secretReplicator := instrumentation.NewNewrelicSecretReplicator(logger, client) - instrumentationLocator := instrumentation.NewNewRelicInstrumentationLocator(logger, client, operatorNamespace) - mgr.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{ - Handler: webhookhandler.NewWebhookHandler( - cfg, ctrl.Log.WithName("pod-webhook"), mgr.GetClient(), admission.NewDecoder(mgr.GetScheme()), - []webhookhandler.PodMutator{ - instrumentation.NewMutator( - logger, - client, - injector, - secretReplicator, - instrumentationLocator, - operatorNamespace, - ), - }, - ), - }) + // Register the Pod mutation webhook + if err = webhook.SetupWebhookWithManager(mgr, operatorNamespace, ctrl.Log.WithName("mutation-webhook")); err != nil { + setupLog.Error(err, "unable to register pod mutate webhook") + os.Exit(1) + } } else { ctrl.Log.Info("Webhooks are disabled, operator is running an unsupported mode", "ENABLE_WEBHOOKS", "false") } @@ -300,22 +276,3 @@ func addDependencies(_ context.Context, mgr ctrl.Manager, cfg config.Config) err } return nil } - -// This function get the option from command argument (tlsConfig), check the validity through k8sapiflag -// and set the config for webhook server. -// refer to https://pkg.go.dev/k8s.io/component-base/cli/flag -func tlsConfigSetting(cfg *tls.Config, tlsOpt tlsConfig) { - // TLSVersion helper function returns the TLS Version ID for the version name passed. - version, err := k8sapiflag.TLSVersion(tlsOpt.minVersion) - if err != nil { - setupLog.Error(err, "TLS version invalid") - } - cfg.MinVersion = version - - // TLSCipherSuites helper function returns a list of cipher suite IDs from the cipher suite names passed. - cipherSuiteIDs, err := k8sapiflag.TLSCipherSuites(tlsOpt.cipherSuites) - if err != nil { - setupLog.Error(err, "Failed to convert TLS cipher suite name to ID") - } - cfg.CipherSuites = cipherSuiteIDs -}