From 82cb3a3b2c7331337c0ffd2934b4f8a381294658 Mon Sep 17 00:00:00 2001 From: sagar-mamdapure Date: Thu, 23 Apr 2026 16:32:28 +0530 Subject: [PATCH] feat(argus,csc): release argus version 15.1.0 & csc version 12.3.0 --- charts/argus/Chart.yaml | 6 +- charts/argus/templates/_helpers.tpl | 75 ++++- charts/argus/templates/collectorset.yaml | 10 +- charts/argus/templates/deployment.yaml | 4 +- charts/argus/templates/post-install.yaml | 288 ------------------ charts/argus/templates/rbac.yaml | 4 - charts/argus/values.schema.json | 123 +------- charts/argus/values.yaml | 8 - charts/collectorset-controller/Chart.yaml | 6 +- .../templates/_helpers.tpl | 60 +++- .../templates/csc-rbac.yaml | 1 - .../templates/deployment.yaml | 4 +- .../templates/post-install.yaml | 222 -------------- .../values.schema.json | 104 ------- charts/collectorset-controller/values.yaml | 9 - 15 files changed, 152 insertions(+), 772 deletions(-) delete mode 100644 charts/argus/templates/post-install.yaml delete mode 100644 charts/collectorset-controller/templates/post-install.yaml diff --git a/charts/argus/Chart.yaml b/charts/argus/Chart.yaml index a16a05e..72c0efc 100644 --- a/charts/argus/Chart.yaml +++ b/charts/argus/Chart.yaml @@ -6,11 +6,11 @@ maintainers: - email: argus@logicmonitor.com name: LogicMonitor name: argus -version: 15.0.1 +version: 15.1.0 home: https://logicmonitor.github.io/helm-charts -appVersion: v18.0.0 +appVersion: v18.1.0 dependencies: - name: lmutil repository: https://logicmonitor.github.io/helm-charts # repository: file://../lmutil - version: 0.1.10 + version: 0.1.11 diff --git a/charts/argus/templates/_helpers.tpl b/charts/argus/templates/_helpers.tpl index 44d0552..c341eaf 100644 --- a/charts/argus/templates/_helpers.tpl +++ b/charts/argus/templates/_helpers.tpl @@ -160,12 +160,20 @@ capabilities: {{/* LM Credentials and Proxy Details. -The user can provide proxy details in values.yaml or by creating user defined secret. -Argus proxy takes precendence over the global proxy. We need to check if the user defined secret contains -Argus proxy details or not, for this we're using Lookup function in helm. + +These env vars are emitted statically into the Deployment so the manifest is +fully declarative and GitOps tools (Rancher Fleet, Argo CD) do not see drift. +This replaces a post-install Job that previously patched the Deployment env +based on a runtime lookup of the user-defined Secret. + +Precedence enforced by the Argus binary (see pkg/config/config.go, +applySecretEnvOverrides): values from the user-defined Secret win over the +plaintext values rendered from .Values. Every `*_FROM_SECRET` env var uses +`optional: true` so a key missing from the user-defined Secret simply leaves +the override unset — Argus then falls back to the plaintext env. */}} -{{- define "lm-credentials-and-proxy-details" -}} +{{- define "argus.lm-credentials-and-proxy-details" -}} - name: ACCESS_ID valueFrom: secretKeyRef: @@ -181,14 +189,69 @@ Argus proxy details or not, for this we're using Lookup function in helm. secretKeyRef: name: {{ include "lmutil.secret-name" . }} key: account -{{- if or .Values.proxy.user .Values.global.proxy.user }} +{{- /* COMPANY_DOMAIN: secret override (if userDefinedSecret) wins, else plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: COMPANY_DOMAIN_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: companyDomain + optional: true +{{- end }} +- name: COMPANY_DOMAIN + value: {{ .Values.global.companyDomain | default "logicmonitor.com" | quote }} +{{- /* ETCD_DISCOVERY_TOKEN: optional, sourced from secret only */}} +{{- if .Values.global.userDefinedSecret }} +- name: ETCD_DISCOVERY_TOKEN_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: etcdDiscoveryToken + optional: true +{{- else }} +- name: ETCD_DISCOVERY_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "lmutil.secret-name" . }} + key: etcdDiscoveryToken + optional: true +{{- end }} +{{- /* PROXY_USER: argus-specific secret key wins over generic, then plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: PROXY_USER_FROM_SECRET_ARGUS + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: argusProxyUser + optional: true +- name: PROXY_USER_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: proxyUser + optional: true +{{- else if or .Values.proxy.user .Values.global.proxy.user }} - name: PROXY_USER valueFrom: secretKeyRef: name: {{ include "lmutil.secret-name" . }} key: proxyUser {{- end }} -{{- if or .Values.proxy.pass .Values.global.proxy.pass }} +{{- /* PROXY_PASS: argus-specific secret key wins over generic, then plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: PROXY_PASS_FROM_SECRET_ARGUS + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: argusProxyPass + optional: true +- name: PROXY_PASS_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: proxyPass + optional: true +{{- else if or .Values.proxy.pass .Values.global.proxy.pass }} - name: PROXY_PASS valueFrom: secretKeyRef: diff --git a/charts/argus/templates/collectorset.yaml b/charts/argus/templates/collectorset.yaml index f2643a6..80325ec 100644 --- a/charts/argus/templates/collectorset.yaml +++ b/charts/argus/templates/collectorset.yaml @@ -34,7 +34,15 @@ spec: {{- $proxyUser := .Values.collector.proxy.user | default "" }} {{- $globalProxyUser := .Values.global.proxy.user | default "" }} {{- $userDefinedSecret := .Values.global.userDefinedSecret | default "" }} - {{- if and (or $proxyUser $globalProxyUser) (empty $userDefinedSecret) }} + {{- /* + When userDefinedSecret is set, always reference it as the collector's secretName so + the CollectorSet can pick up proxy/credentials keys from it. This used to be done at + runtime by a post-install Job (`kubectl patch collectorset ... secretName: `), + which caused drift in Rancher Fleet/Argo CD. Inlining it keeps the manifest declarative. + */ -}} + {{- if $userDefinedSecret }} + secretName: {{ $userDefinedSecret }} + {{- else if or $proxyUser $globalProxyUser }} secretName: {{ include "lmutil.fullname" . }}-collector {{- end }} {{- if .Values.collector.proxy.url }} diff --git a/charts/argus/templates/deployment.yaml b/charts/argus/templates/deployment.yaml index 436b889..10bc116 100644 --- a/charts/argus/templates/deployment.yaml +++ b/charts/argus/templates/deployment.yaml @@ -10,7 +10,7 @@ metadata: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} spec: - replicas: 0 + replicas: {{ .Values.replicas | default 1 }} selector: matchLabels: {{ include "lmutil.selectorLabels" . | nindent 6 }} @@ -85,7 +85,7 @@ spec: value: {{ template "lmutil.name" . }} - name: APP_KUBERNETES_IO_INSTANCE value: {{ .Release.Name }} - {{ include "lm-credentials-and-proxy-details" . | nindent 12 }} + {{ include "argus.lm-credentials-and-proxy-details" . | nindent 12 }} volumes: - name: collector-ids configMap: diff --git a/charts/argus/templates/post-install.yaml b/charts/argus/templates/post-install.yaml deleted file mode 100644 index adfffd0..0000000 --- a/charts/argus/templates/post-install.yaml +++ /dev/null @@ -1,288 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: argus-props-patch-{{ .Release.Name }} - labels: - {{- include "argus.labels" . | nindent 4 }} - app.kubernetes.io/job: config-updater - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation - {{- include "argus.annotations" . | nindent 4 }} - -spec: - ttlSecondsAfterFinished: {{ .Values.patchJob.ttlSecondsAfterFinished | default 1800 }} # delete this job from cluster after 30 mins - template: - spec: - containers: - - name: patch-configmap - image: {{ include "lmutil.patchjob-image" . }} - imagePullPolicy: {{ .Values.patchJob.image.pullPolicy | default (.Values.global.image.pullPolicy | default "IfNotPresent") }} - command: - - /bin/sh - - -c - args: - - | - set -e - echo "Patching config.yaml inside ConfigMap..." - kubectl get configmap {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} -o yaml | yq '.data["config.yaml"]' >> /tmp/new-config.yaml - - KSM_URL="{{ .Values.ksmUrl }}" - if [ -n "$KSM_URL" ] && [ "$KSM_URL" != "null" ]; then - echo "Using user-provided kube-state-metrics URL: $KSM_URL" - else - echo "🔍 Checking for kube-state-metrics in namespace {{ .Release.Namespace }}..." - - # First, check in the current namespace - KSM_SVC=$(kubectl get svc -n {{ .Release.Namespace }} -l app.kubernetes.io/name=kube-state-metrics -o json | jq -r '.items[0]') - - if [ -n "$KSM_SVC" ] && [ "$KSM_SVC" != "null" ]; then - echo "✅ Found kube-state-metrics in namespace {{ .Release.Namespace }}" - else - echo "⚠️ Not found in {{ .Release.Namespace }}, searching all namespaces..." - - # Search all namespaces - KSM_SVC=$(kubectl get svc --all-namespaces -l app.kubernetes.io/name=kube-state-metrics -o json | jq -r '.items[0]') - - if [ -z "$KSM_SVC" ] || [ "$KSM_SVC" = "null" ]; then - echo "❌ kube-state-metrics service not found anywhere." - # exit 1 - fi - fi - - # Extract namespace, ClusterIP, and port - SERVICE_NAME=$(echo "$KSM_SVC" | jq -r '.metadata.name') - KSM_NAMESPACE=$(echo "$KSM_SVC" | jq -r '.metadata.namespace') - CLUSTER_IP=$(echo "$KSM_SVC" | jq -r '.spec.clusterIP') - PORT=$(echo "$KSM_SVC" | jq -r '.spec.ports[0].port') - - # Validate all required variables are populated - if [ -z "$SERVICE_NAME" ] || [ -z "$KSM_NAMESPACE" ] || [ -z "$CLUSTER_IP" ] || [ -z "$PORT" ]; then - echo "❌ One or more kube-state-metrics service variables are empty." - echo "SERVICE_NAME: $SERVICE_NAME" - echo "KSM_NAMESPACE: $KSM_NAMESPACE" - echo "CLUSTER_IP: $CLUSTER_IP" - echo "PORT: $PORT" - fi - - # Construct metrics endpoint - METRICS_ENDPOINT="http://${SERVICE_NAME}.${KSM_NAMESPACE}.svc.cluster.local:${PORT}/metrics" - echo "📡 kube-state-metrics endpoint: $METRICS_ENDPOINT" - - yq e -i ".ksmUrl = \"${METRICS_ENDPOINT}\"" /tmp/new-config.yaml - - # Use yq to create clean patch without quote escaping issues - yq eval -n '{"data": {"config.yaml": strload("/tmp/new-config.yaml")}}' -o json > /tmp/patch.json - kubectl patch configmap {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} --type merge --patch-file /tmp/patch.json - - UPDATED=$(kubectl get configmap {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} -o yaml | yq '.data["config.yaml"]') - - echo "ConfigMap patched successfully. $UPDATED" - fi - - # Initialize variables with global scope (use export to make them available to subshells) - export SECRET_DATA="" - export HAS_COMPANY_DOMAIN="false" - export COMPANY_DOMAIN_VALUE="" - - # Get data from secret whose name is .Values.global.userDefinedSecret - if [ -n "{{ .Values.global.userDefinedSecret }}" ]; then - echo "Looking up secret {{ .Values.global.userDefinedSecret }} in namespace {{ .Release.Namespace }}" - export SECRET_DATA=$(kubectl get secret {{ .Values.global.userDefinedSecret }} -n {{ .Release.Namespace }} -o json | jq -r '.data') - if [ "$SECRET_DATA" != "null" ] && [ -n "$SECRET_DATA" ]; then - echo "Secret data found" - - # Check if SECRET_DATA contains a key named companyDomain - if echo "$SECRET_DATA" | jq -e 'has("companyDomain")' > /dev/null; then - echo "Secret contains companyDomain key" - export HAS_COMPANY_DOMAIN="true" - export COMPANY_DOMAIN_VALUE="$(echo "$SECRET_DATA" | jq -r '.companyDomain' | base64 -d)" - echo "Company domain from secret: $COMPANY_DOMAIN_VALUE" - else - echo "Secret does not contain companyDomain key" - fi - - # This creates a variable for each key in the secret - for key in $(echo $SECRET_DATA | jq -r 'keys[]'); do - value=$(echo $SECRET_DATA | jq -r ".[\"$key\"]" | base64 -d) - # Create a variable with uppercase key name and export it - export "SECRET_$(echo $key | tr '[:lower:]' '[:upper:]')=$value" - echo "Created variable SECRET_$(echo $key | tr '[:lower:]' '[:upper:]')" - done - else - echo "No data found in secret {{ .Values.global.userDefinedSecret }}" - fi - - # Validate accessKey, accessId, account to be present in secret - if [ -z "$SECRET_ACCESSKEY" ] || [ -z "$SECRET_ACCESSID" ] || [ -z "$SECRET_ACCOUNT" ]; then - echo "❌ accessKey, accessId, or account not found in secret {{ .Values.global.userDefinedSecret }}. These are required." - exit 1 - fi - else - echo "No userDefinedSecret specified in values" - fi - - - # Update the deployment with environment variables - # Use the global HAS_COMPANY_DOMAIN variable to conditionally include company domain - PATCH_CONTENT="" - ENVS="" - - # Determine where to get company domain from - if [ "$HAS_COMPANY_DOMAIN" = "true" ]; then - echo "Using company domain from secret {{ .Values.global.userDefinedSecret }}" - COMPANY_DOMAIN_ENV=$(cat << EOF - { - "name": "COMPANY_DOMAIN", - "valueFrom": { - "secretKeyRef": { - "name": "{{ .Values.global.userDefinedSecret }}", - "key": "companyDomain" - } - } - } - EOF - ) - else - echo "Using company domain from .Values" - COMPANY_DOMAIN_ENV=$(cat << EOF - { - "name": "COMPANY_DOMAIN", - "value": {{ .Values.global.companyDomain | default "logicmonitor.com" | quote }} - } - EOF - ) - fi - - ENVS="${COMPANY_DOMAIN_ENV}" - # Determine if etcdDiscoveryToken is set - if [ "$SECRET_ETCDDISCOVERYTOKEN" != "" ]; then - echo "etcdDiscoveryToken found in secret, adding to env" - ETCD_ENV=$(cat << EOF - { - "name": "ETCD_DISCOVERY_TOKEN", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "etcdDiscoveryToken" - } - } - } - EOF - ) - ENVS="${ENVS},${ETCD_ENV}" - fi - - # Determine if proxyUser and proxyPass are set - if [ "$SECRET_ARGUS_PROXYUSER" != "" ]; then - echo "Argus proxyUser found in secret, adding to env" - PROXY_USER_ENV=$(cat << EOF - { - "name": "PROXY_USER", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "argusProxyUser" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_USER_ENV}" - fi - - if [ "$SECRET_PROXYUSER" != "" ]; then - echo "ProxyUser found in secret, adding to env" - PROXY_USER_ENV=$(cat << EOF - { - "name": "PROXY_USER", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "proxyUser" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_USER_ENV}" - fi - - if [ "$SECRET_ARGUS_PROXYPASS" != "" ]; then - echo "Argus proxyPass found in secret, adding to env" - PROXY_PASS_ENV=$(cat << EOF - { - "name": "PROXY_PASS", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "argusProxyPass" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_PASS_ENV}" - fi - - if [ "$SECRET_PROXYPASS" != "" ]; then - echo "ProxyPass found in secret, adding to env" - PROXY_PASS_ENV=$(cat << EOF - { - "name": "PROXY_PASS", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "proxyPass" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_PASS_ENV}" - fi - - # Create the full patch with proper JSON formatting - PATCH_CONTENT=$(cat << EOF - { - "spec": { - "template": { - "spec": { - "containers": [ - { - "name": "argus", - "env": [ - ${ENVS} - ] - } - ] - } - } - } - } - EOF - ) - - # Apply the patch - echo "Applying deployment patch..." - kubectl patch deployment {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} --type=strategic --patch "$PATCH_CONTENT" - - # Scale the deployment - kubectl scale deployment {{ include "lmutil.fullname" . }} --replicas={{ .Values.replicas }} -n {{ .Release.Namespace }} - - # Patch collectorset if proxyUser or collectorProxyUser is found in userDefinedSecret - if [ -n "{{ .Values.global.userDefinedSecret }}" ] && [ "$SECRET_DATA" != "null" ] && [ -n "$SECRET_DATA" ]; then - if echo "$SECRET_DATA" | jq -e 'has("collectorProxyUser") or has("proxyUser")' > /dev/null; then - echo "collectorProxyUser or proxyUser found in userDefinedSecret, patching collectorset with userDefinedSecret name..." - kubectl patch collectorset {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} --type=merge -p '{"spec":{"secretName":"{{ .Values.global.userDefinedSecret }}"}}' - fi - else - echo "No collectorset secretName patch required." - fi - env: - - name: HOME - value: /tmp - - name: GODEBUG - value: boringcrypto=1 - restartPolicy: Never - serviceAccountName: {{ include "lmutil.serviceAccountName" . }} \ No newline at end of file diff --git a/charts/argus/templates/rbac.yaml b/charts/argus/templates/rbac.yaml index da960d1..f9cf1f3 100644 --- a/charts/argus/templates/rbac.yaml +++ b/charts/argus/templates/rbac.yaml @@ -70,7 +70,6 @@ rules: verbs: - list - get - - patch - watch - update - create @@ -79,7 +78,6 @@ rules: - "apps" resources: - deployments - - deployments/scale - daemonsets - replicasets - statefulsets @@ -88,7 +86,6 @@ rules: - get - watch - update - - patch - apiGroups: - "scheduling.k8s.io" resources: @@ -128,7 +125,6 @@ rules: - list - get - watch - - patch - update - apiGroups: - "batch" diff --git a/charts/argus/values.schema.json b/charts/argus/values.schema.json index 95c83ec..80b3899 100644 --- a/charts/argus/values.schema.json +++ b/charts/argus/values.schema.json @@ -114,16 +114,6 @@ "flushIntervalMs": 2000 } }, - "patchJob": { - "ttlSecondsAfterFinished": 1800, - "image": { - "registry": "", - "repository": "", - "name": "kubectl", - "tag": "", - "pullPolicy": "" - } - }, "selfMonitor": { "enable": false, "port": 2112 @@ -1447,99 +1437,6 @@ }, "additionalProperties": false }, - "patchJob": { - "$id": "#/properties/patchJob", - "$comment": "tf:optional", - "type": "object", - "title": "The Argus Props Patch Job Schema", - "description": "Configurations for Argus Props Patch Job", - "default": {}, - "examples": [ - { - "ttlSecondsAfterFinished": 1800, - "image": { - "registry": "", - "repository": "", - "name": "kubectl", - "tag": "", - "pullPolicy": "" - } - } - ], - "required": [], - "properties": { - "ttlSecondsAfterFinished": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/ttlSecondsAfterFinished", - "type": "integer", - "title": "Time To Live After PatchJob Completion", - "description": "Specifies the time-to-live (in seconds) for the patchJob resource after it has completed execution. Once this duration has passed, the job and its associated pods will be automatically cleaned up by Kubernetes", - "default": 1800, - "examples": [ - 60, - 1800 - ] - }, - "image": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image", - "type": "object", - "title": "The patchJob image schema", - "description": "Container image configuration for the patchJob", - "default": {}, - "properties": { - "registry": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/registry", - "type": "string", - "title": "The registry schema", - "description": "Container image registry for patchJob", - "default": "", - "examples": ["public.ecr.aws/logicmonitor"] - }, - "repository": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/repository", - "type": "string", - "title": "The repository schema", - "description": "Container image repository for patchJob. Defaults to 'logicmonitor' when no registry is set.", - "default": "", - "examples": ["logicmonitor"] - }, - "name": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/name", - "type": "string", - "title": "The name schema", - "description": "Container image name for patchJob", - "default": "kubectl", - "examples": ["kubectl"] - }, - "tag": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/tag", - "type": "string", - "title": "The tag schema", - "description": "Container image tag for patchJob. Defaults to version defined in lmutil.patchjob-image template", - "default": "", - "examples": ["v1.29.3"] - }, - "pullPolicy": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/pullPolicy", - "type": "string", - "title": "The pullPolicy schema", - "description": "Container image pull policy for patchJob. Defaults to global.image.pullPolicy or 'IfNotPresent'", - "default": "", - "enum": ["Always", "IfNotPresent", "Never", ""], - "examples": ["IfNotPresent"] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, "jobs": { "$id": "#/properties/jobs", "$comment": "tf:optional", @@ -3391,7 +3288,7 @@ }, "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement": { "required": [ - "key", + "key", "operator" ], "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", @@ -3406,7 +3303,7 @@ }, "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", "type": "array" - }, + }, "key": { "description": "key is the label key that the selector applies to.", "type": "string" @@ -3415,7 +3312,7 @@ "type": "object" }, "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector": { - "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", "properties": { "matchLabels": { "additionalProperties": { @@ -3436,7 +3333,7 @@ }, "io.k8s.kubernetes.pkg.api.v1.WeightedPodAffinityTerm": { "required": [ - "weight", + "weight", "podAffinityTerm" ], "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", @@ -3457,25 +3354,25 @@ "required": [ "topologyKey" ], - "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key tches that of any node on which a pod of the set of pods is running", + "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key tches that of any node on which a pod of the set of pods is running", "properties": { "labelSelector": { - "description": "A label query over a set of resources, in this case pods.", + "description": "A label query over a set of resources, in this case pods.", "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" - }, + }, "namespaces": { "items": { "type": "string" }, "description": "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\"", "type": "array" - }, + }, "topologyKey": { "type": "string", "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. For PreferredDuringScheduling pod anti-affinity, empty topologyKey is interpreted as \"all topologies\" (\"all topologies\" here means all the topologyKeys indicated by scheduler command-line argument --failure-domains); for affinity and for RequiredDuringScheduling pod anti-affinity, empty topologyKey is not allowed." }, "namespaceSelector": { - "description": "A label query over a set of resources, in this case pods.", + "description": "A label query over a set of resources, in this case pods.", "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" } }, @@ -3614,7 +3511,7 @@ }, "type": "array", "description": "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system will try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. RequiredDuringSchedulingRequiredDuringExecution []PodAffinityTerm `json:\"requiredDuringSchedulingRequiredDuringExecution,omitempty\"` If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied." - }, + }, "preferredDuringSchedulingIgnoredDuringExecution": { "items": { "$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.WeightedPodAffinityTerm" diff --git a/charts/argus/values.yaml b/charts/argus/values.yaml index c1ab967..d63d248 100644 --- a/charts/argus/values.yaml +++ b/charts/argus/values.yaml @@ -165,14 +165,6 @@ jobs: bufferSize: 100 batchSize: 10 flushIntervalMs: 2000 -patchJob: - ttlSecondsAfterFinished: 1800 # delete this job from cluster after 30 mins - image: - registry: "" # Optional: e.g., "public.ecr.aws/logicmonitor" - repository: "" # Optional: only needed if registry requires additional path segment. Defaults to "logicmonitor" when no registry is set. - name: "kubectl" - tag: "" # Defaults to version defined in lmutil.patchjob-image template - pullPolicy: "" # Defaults to global.image.pullPolicy or "IfNotPresent" selfMonitor: enable: false port: 2112 diff --git a/charts/collectorset-controller/Chart.yaml b/charts/collectorset-controller/Chart.yaml index ba1791e..ae14a5d 100644 --- a/charts/collectorset-controller/Chart.yaml +++ b/charts/collectorset-controller/Chart.yaml @@ -6,14 +6,14 @@ maintainers: - email: argus@logicmonitor.com name: LogicMonitor name: collectorset-controller -version: 12.2.0 +version: 12.3.0 home: https://logicmonitor.github.io/helm-charts -appVersion: v13.4.0 +appVersion: v13.5.0 dependencies: - name: lmutil repository: https://logicmonitor.github.io/helm-charts # repository: file://../lmutil - version: 0.1.10 + version: 0.1.11 annotations: artifacthub.io/crds: | - kind: CollectorSet diff --git a/charts/collectorset-controller/templates/_helpers.tpl b/charts/collectorset-controller/templates/_helpers.tpl index 0df6df2..9dada87 100644 --- a/charts/collectorset-controller/templates/_helpers.tpl +++ b/charts/collectorset-controller/templates/_helpers.tpl @@ -61,12 +61,21 @@ Create the name of the service account to use {{/* LM Credentials and Proxy Details. -The user can provide proxy details in values.yaml or by creating user defined secret. -Argus proxy takes precendence over the global proxy. We need to check if the user defined secret contains -Argus proxy details or not, for this we're using Lookup function in helm. + +These env vars are emitted statically into the Deployment so the manifest is +fully declarative and GitOps tools (Rancher Fleet, Argo CD) do not see drift. +This replaces a post-install Job that previously patched the Deployment env +based on a runtime lookup of the user-defined Secret. + +Precedence enforced by the collectorset-controller binary (see +pkg/config/config.go, applySecretEnvOverrides): values from the user-defined +Secret win over the plaintext values rendered from .Values. Every +`*_FROM_SECRET` env var uses `optional: true` so a key missing from the +user-defined Secret simply leaves the override unset — the binary then falls +back to the plaintext env. */}} -{{- define "lm-credentials-and-proxy-details" -}} +{{- define "collectorset-controller.lm-credentials-and-proxy-details" -}} - name: ACCESS_ID valueFrom: secretKeyRef: @@ -82,14 +91,53 @@ Argus proxy details or not, for this we're using Lookup function in helm. secretKeyRef: name: {{ include "lmutil.secret-name" . }} key: account -{{- if or .Values.proxy.user .Values.global.proxy.user }} +{{- /* COMPANY_DOMAIN: secret override (if userDefinedSecret) wins, else plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: COMPANY_DOMAIN_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: companyDomain + optional: true +{{- end }} +- name: COMPANY_DOMAIN + value: {{ .Values.global.companyDomain | default "logicmonitor.com" | quote }} +{{- /* PROXY_USER: csc-specific secret key wins over generic, then plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: PROXY_USER_FROM_SECRET_CSC + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: collectorProxyUser + optional: true +- name: PROXY_USER_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: proxyUser + optional: true +{{- else if or .Values.proxy.user .Values.global.proxy.user }} - name: PROXY_USER valueFrom: secretKeyRef: name: {{ include "lmutil.secret-name" . }} key: proxyUser {{- end }} -{{- if or .Values.proxy.pass .Values.global.proxy.pass }} +{{- /* PROXY_PASS: csc-specific secret key wins over generic, then plaintext */}} +{{- if .Values.global.userDefinedSecret }} +- name: PROXY_PASS_FROM_SECRET_CSC + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: collectorProxyPass + optional: true +- name: PROXY_PASS_FROM_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.global.userDefinedSecret }} + key: proxyPass + optional: true +{{- else if or .Values.proxy.pass .Values.global.proxy.pass }} - name: PROXY_PASS valueFrom: secretKeyRef: diff --git a/charts/collectorset-controller/templates/csc-rbac.yaml b/charts/collectorset-controller/templates/csc-rbac.yaml index c948673..0476f2e 100644 --- a/charts/collectorset-controller/templates/csc-rbac.yaml +++ b/charts/collectorset-controller/templates/csc-rbac.yaml @@ -36,7 +36,6 @@ rules: resources: - statefulsets - deployments - - deployments/scale verbs: - "*" - apiGroups: diff --git a/charts/collectorset-controller/templates/deployment.yaml b/charts/collectorset-controller/templates/deployment.yaml index 3363a72..16aada8 100644 --- a/charts/collectorset-controller/templates/deployment.yaml +++ b/charts/collectorset-controller/templates/deployment.yaml @@ -10,7 +10,7 @@ metadata: checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} {{- include "collectorset-controller.annotations" . | nindent 4}} spec: - replicas: 0 + replicas: 1 selector: matchLabels: {{- include "lmutil.selectorLabels" . | nindent 6 }} @@ -81,7 +81,7 @@ spec: {{ toYaml . | nindent 12 }} {{- end}} env: - {{ include "lm-credentials-and-proxy-details" . | nindent 12 }} + {{ include "collectorset-controller.lm-credentials-and-proxy-details" . | nindent 12 }} volumeMounts: - name: etc-collectorset-controller mountPath: "/etc/collectorset-controller" diff --git a/charts/collectorset-controller/templates/post-install.yaml b/charts/collectorset-controller/templates/post-install.yaml deleted file mode 100644 index 6ddf4fc..0000000 --- a/charts/collectorset-controller/templates/post-install.yaml +++ /dev/null @@ -1,222 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: csc-props-patch-{{ .Release.Name }} - labels: - {{- include "collectorset-controller.labels" . | nindent 4}} - app.kubernetes.io/job: config-updater - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation - {{- include "collectorset-controller.annotations" . | nindent 4}} -spec: - ttlSecondsAfterFinished: {{ .Values.patchJob.ttlSecondsAfterFinished | default 1800 }} # delete this job from cluster after 30 mins - template: - spec: - containers: - - name: patch-configmap - image: {{ include "lmutil.patchjob-image" . }} - imagePullPolicy: {{ .Values.patchJob.image.pullPolicy | default (.Values.global.image.pullPolicy | default "IfNotPresent") }} - command: - - /bin/sh - - -c - args: - - | - set -e - - # Initialize variables with global scope (use export to make them available to subshells) - export SECRET_DATA="" - export HAS_COMPANY_DOMAIN="false" - export COMPANY_DOMAIN_VALUE="" - - # Get data from secret whose name is .Values.global.userDefinedSecret - if [ -n "{{ .Values.global.userDefinedSecret }}" ]; then - echo "Looking up secret {{ .Values.global.userDefinedSecret }} in namespace {{ .Release.Namespace }}" - export SECRET_DATA=$(kubectl get secret {{ .Values.global.userDefinedSecret }} -n {{ .Release.Namespace }} -o json | jq -r '.data') - if [ "$SECRET_DATA" != "null" ] && [ -n "$SECRET_DATA" ]; then - echo "Secret data found" - - # Check if SECRET_DATA contains a key named companyDomain - if echo "$SECRET_DATA" | jq -e 'has("companyDomain")' > /dev/null; then - echo "Secret contains companyDomain key" - export HAS_COMPANY_DOMAIN="true" - export COMPANY_DOMAIN_VALUE="$(echo "$SECRET_DATA" | jq -r '.companyDomain' | base64 -d)" - echo "Company domain from secret: $COMPANY_DOMAIN_VALUE" - else - echo "Secret does not contain companyDomain key" - fi - - # This creates a variable for each key in the secret - for key in $(echo $SECRET_DATA | jq -r 'keys[]'); do - value=$(echo $SECRET_DATA | jq -r ".[\"$key\"]" | base64 -d) - # Create a variable with uppercase key name and export it - export "SECRET_$(echo $key | tr '[:lower:]' '[:upper:]')=$value" - echo "Created variable SECRET_$(echo $key | tr '[:lower:]' '[:upper:]')" - done - else - echo "No data found in secret {{ .Values.global.userDefinedSecret }}" - fi - - # Validate accessKey, accessId, account to be present in secret - if [ -z "$SECRET_ACCESSKEY" ] || [ -z "$SECRET_ACCESSID" ] || [ -z "$SECRET_ACCOUNT" ]; then - echo "❌ accessKey, accessId, or account not found in secret {{ .Values.global.userDefinedSecret }}. These are required." - exit 1 - fi - else - echo "No userDefinedSecret specified in values" - fi - - - # Update the deployment with environment variables - # Use the global HAS_COMPANY_DOMAIN variable to conditionally include company domain - PATCH_CONTENT="" - ENVS="" - - # Determine where to get company domain from - if [ "$HAS_COMPANY_DOMAIN" = "true" ]; then - echo "Using company domain from secret {{ .Values.global.userDefinedSecret }}" - COMPANY_DOMAIN_ENV=$(cat << EOF - { - "name": "COMPANY_DOMAIN", - "valueFrom": { - "secretKeyRef": { - "name": "{{ .Values.global.userDefinedSecret }}", - "key": "companyDomain" - } - } - } - EOF - ) - else - echo "Using company domain from .Values" - COMPANY_DOMAIN_ENV=$(cat << EOF - { - "name": "COMPANY_DOMAIN", - "value": {{ .Values.global.companyDomain | default "logicmonitor.com" | quote }} - } - EOF - ) - fi - - ENVS="${COMPANY_DOMAIN_ENV}" - # Determine if etcdDiscoveryToken is set - if [ "$SECRET_ETCDDISCOVERYTOKEN" != "" ]; then - echo "etcdDiscoveryToken found in secret, adding to env" - ETCD_ENV=$(cat << EOF - { - "name": "ETCD_DISCOVERY_TOKEN", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "etcdDiscoveryToken" - } - } - } - EOF - ) - ENVS="${ENVS},${ETCD_ENV}" - fi - - # Determine if proxyUser and proxyPass are set - if [ "$SECRET_ARGUS_PROXYUSER" != "" ]; then - echo "Argus proxyUser found in secret, adding to env" - PROXY_USER_ENV=$(cat << EOF - { - "name": "PROXY_USER", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "argusProxyUser" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_USER_ENV}" - fi - - if [ "$SECRET_PROXYUSER" != "" ]; then - echo "ProxyUser found in secret, adding to env" - PROXY_USER_ENV=$(cat << EOF - { - "name": "PROXY_USER", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "proxyUser" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_USER_ENV}" - fi - - if [ "$SECRET_ARGUS_PROXYPASS" != "" ]; then - echo "Argus proxyPass found in secret, adding to env" - PROXY_PASS_ENV=$(cat << EOF - { - "name": "PROXY_PASS", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "argusProxyPass" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_PASS_ENV}" - fi - - if [ "$SECRET_PROXYPASS" != "" ]; then - echo "ProxyPass found in secret, adding to env" - PROXY_PASS_ENV=$(cat << EOF - { - "name": "PROXY_PASS", - "valueFrom": { - "secretKeyRef": { - "name": "{{ include "lmutil.secret-name" . }}", - "key": "proxyPass" - } - } - } - EOF - ) - ENVS="${ENVS},${PROXY_PASS_ENV}" - fi - - # Create the full patch with proper JSON formatting - PATCH_CONTENT=$(cat << EOF - { - "spec": { - "template": { - "spec": { - "containers": [ - { - "name": "collectorset-controller", - "env": [ - ${ENVS} - ] - } - ] - } - } - } - } - EOF - ) - - # Apply the patch - echo "Applying deployment patch..." - kubectl patch deployment {{ include "lmutil.fullname" . }} -n {{ .Release.Namespace }} --type=strategic --patch "$PATCH_CONTENT" - - # Scale the deployment - kubectl scale deployment {{ include "lmutil.fullname" . }} --replicas=1 -n {{ .Release.Namespace }} - env: - - name: HOME - value: /tmp - - name: GODEBUG - value: boringcrypto=1 - restartPolicy: Never - serviceAccountName: {{ include "lmutil.serviceAccountName" . }} \ No newline at end of file diff --git a/charts/collectorset-controller/values.schema.json b/charts/collectorset-controller/values.schema.json index 0201bb1..110a618 100644 --- a/charts/collectorset-controller/values.schema.json +++ b/charts/collectorset-controller/values.schema.json @@ -39,16 +39,6 @@ "labels": {}, "annotations": {}, "ignoreSSL": false, - "patchJob": { - "ttlSecondsAfterFinished": 1800, - "image": { - "registry": "", - "repository": "", - "name": "kubectl", - "tag": "", - "pullPolicy": "" - } - }, "global": { "accessID": "", "accessKey": "", @@ -524,100 +514,6 @@ false ] }, - "patchJob": { - "$id": "#/properties/patchJob", - "$comment": "tf:optional", - "type": "object", - "title": "The CollectorSet Props Patch Job Schema", - "description": "Configurations for CollectorSet Props Patch Job", - "default": {}, - "examples": [ - { - "ttlSecondsAfterFinished": 1800, - "image": { - "registry": "", - "repository": "", - "name": "kubectl", - "tag": "", - "pullPolicy": "" - } - } - ], - "required": [ - ], - "properties": { - "ttlSecondsAfterFinished": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/ttlSecondsAfterFinished", - "type": "integer", - "title": "Time To Live After PatchJob Completion", - "description": "Specifies the time-to-live (in seconds) for the patchJob resource after it has completed execution. Once this duration has passed, the job and its associated pods will be automatically cleaned up by Kubernetes", - "default": 1800, - "examples": [ - 60, - 1800 - ] - }, - "image": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image", - "type": "object", - "title": "The patchJob image schema", - "description": "Container image configuration for the patchJob", - "default": {}, - "properties": { - "registry": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/registry", - "type": "string", - "title": "The registry schema", - "description": "Container image registry for patchJob", - "default": "", - "examples": ["public.ecr.aws/logicmonitor"] - }, - "repository": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/repository", - "type": "string", - "title": "The repository schema", - "description": "Container image repository for patchJob. Defaults to 'logicmonitor' when no registry is set.", - "default": "", - "examples": ["logicmonitor"] - }, - "name": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/name", - "type": "string", - "title": "The name schema", - "description": "Container image name for patchJob", - "default": "kubectl", - "examples": ["kubectl"] - }, - "tag": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/tag", - "type": "string", - "title": "The tag schema", - "description": "Container image tag for patchJob. Defaults to version defined in lmutil.patchjob-image template", - "default": "", - "examples": ["v1.29.3"] - }, - "pullPolicy": { - "$comment": "tf:optional", - "$id": "#/properties/patchJob/properties/image/properties/pullPolicy", - "type": "string", - "title": "The pullPolicy schema", - "description": "Container image pull policy for patchJob. Defaults to global.image.pullPolicy or 'IfNotPresent'", - "default": "", - "enum": ["Always", "IfNotPresent", "Never", ""], - "examples": ["IfNotPresent"] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, "global": { "$comment": "tf:optional", "$id": "#/properties/global", diff --git a/charts/collectorset-controller/values.yaml b/charts/collectorset-controller/values.yaml index 036b2b9..e5ff04f 100644 --- a/charts/collectorset-controller/values.yaml +++ b/charts/collectorset-controller/values.yaml @@ -55,15 +55,6 @@ labels: {} annotations: {} # Set flag to ignore ssl/tls validation ignoreSSL: false -patchJob: - ttlSecondsAfterFinished: 1800 # delete this job from cluster after 30 mins - image: - registry: "" # Optional: e.g., "public.ecr.aws/logicmonitor" - repository: "" # Optional: only needed if registry requires additional path segment. Defaults to "logicmonitor" when no registry is set. - name: "kubectl" - tag: "" # Defaults to version defined in lmutil.patchjob-image template - pullPolicy: "" # Defaults to global.image.pullPolicy or "IfNotPresent" - imagePullSecrets: [] global: accessID: ""