diff --git a/deploy/charts/disco-agent/README.md b/deploy/charts/disco-agent/README.md index ec2c3ed9..2b4a69a6 100644 --- a/deploy/charts/disco-agent/README.md +++ b/deploy/charts/disco-agent/README.md @@ -277,6 +277,24 @@ Example: excludeAnnotationKeysRegex: ['^kapp\.k14s\.io/original.*'] > ```yaml > [] > ``` +#### **config.clusterName** ~ `string` +> Default value: +> ```yaml +> "" +> ``` + +A human readable name for the cluster where the agent is deployed (optional). + +This cluster name will be associated with the data that the agent uploads to the Discovery and Context service. If empty (the default), the service account name will be used instead. +#### **config.clusterDescription** ~ `string` +> Default value: +> ```yaml +> "" +> ``` + +A short description of the cluster where the agent is deployed (optional). + +This description will be associated with the data that the agent uploads to the Discovery and Context service. The description may include contact information such as the email address of the cluster administrator, so that any problems and risks identified by the Discovery and Context service can be communicated to the people responsible for the affected secrets. #### **authentication.secretName** ~ `string` > Default value: > ```yaml diff --git a/deploy/charts/disco-agent/templates/configmap.yaml b/deploy/charts/disco-agent/templates/configmap.yaml index 1dea3d10..231a26cd 100644 --- a/deploy/charts/disco-agent/templates/configmap.yaml +++ b/deploy/charts/disco-agent/templates/configmap.yaml @@ -7,6 +7,8 @@ metadata: {{- include "disco-agent.labels" . | nindent 4 }} data: config.yaml: |- + cluster_name: {{ .Values.config.clusterName | quote }} + cluster_description: {{ .Values.config.clusterDescription | quote }} period: {{ .Values.config.period | quote }} {{- with .Values.config.excludeAnnotationKeysRegex }} exclude-annotation-keys-regex: diff --git a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap index f5e30426..2c70df00 100644 --- a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap +++ b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap @@ -1,8 +1,224 @@ +custom-cluster-description: + 1: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "" + cluster_description: "A cloud hosted Kubernetes cluster hosting production workloads.\n\nteam: team-1\nemail: team-1@example.com\npurpose: Production workloads\n" + period: "12h0m0s" + data-gatherers: + - kind: k8s-discovery + name: ark/discovery + - kind: k8s-dynamic + name: ark/secrets + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: k8s-dynamic + name: ark/serviceaccounts + config: + resource-type: + resource: serviceaccounts + version: v1 + - kind: k8s-dynamic + name: ark/roles + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: roles + - kind: k8s-dynamic + name: ark/clusterroles + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: clusterroles + - kind: k8s-dynamic + name: ark/rolebindings + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: rolebindings + - kind: k8s-dynamic + name: ark/clusterrolebindings + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: clusterrolebindings + - kind: k8s-dynamic + name: ark/jobs + config: + resource-type: + version: v1 + group: batch + resource: jobs + - kind: k8s-dynamic + name: ark/cronjobs + config: + resource-type: + version: v1 + group: batch + resource: cronjobs + - kind: k8s-dynamic + name: ark/deployments + config: + resource-type: + version: v1 + group: apps + resource: deployments + - kind: k8s-dynamic + name: ark/statefulsets + config: + resource-type: + version: v1 + group: apps + resource: statefulsets + - kind: k8s-dynamic + name: ark/daemonsets + config: + resource-type: + version: v1 + group: apps + resource: daemonsets + - kind: k8s-dynamic + name: ark/pods + config: + resource-type: + version: v1 + resource: pods + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: disco-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: disco-agent-0.0.0 + name: test-disco-agent-config + namespace: test-ns +custom-cluster-name: + 1: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "cluster-1 region-1 cloud-1 " + cluster_description: "" + period: "12h0m0s" + data-gatherers: + - kind: k8s-discovery + name: ark/discovery + - kind: k8s-dynamic + name: ark/secrets + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: k8s-dynamic + name: ark/serviceaccounts + config: + resource-type: + resource: serviceaccounts + version: v1 + - kind: k8s-dynamic + name: ark/roles + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: roles + - kind: k8s-dynamic + name: ark/clusterroles + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: clusterroles + - kind: k8s-dynamic + name: ark/rolebindings + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: rolebindings + - kind: k8s-dynamic + name: ark/clusterrolebindings + config: + resource-type: + version: v1 + group: rbac.authorization.k8s.io + resource: clusterrolebindings + - kind: k8s-dynamic + name: ark/jobs + config: + resource-type: + version: v1 + group: batch + resource: jobs + - kind: k8s-dynamic + name: ark/cronjobs + config: + resource-type: + version: v1 + group: batch + resource: cronjobs + - kind: k8s-dynamic + name: ark/deployments + config: + resource-type: + version: v1 + group: apps + resource: deployments + - kind: k8s-dynamic + name: ark/statefulsets + config: + resource-type: + version: v1 + group: apps + resource: statefulsets + - kind: k8s-dynamic + name: ark/daemonsets + config: + resource-type: + version: v1 + group: apps + resource: daemonsets + - kind: k8s-dynamic + name: ark/pods + config: + resource-type: + version: v1 + resource: pods + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: disco-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: disco-agent-0.0.0 + name: test-disco-agent-config + namespace: test-ns custom-period: 1: | apiVersion: v1 data: config.yaml: |- + cluster_name: "" + cluster_description: "" period: "1m" data-gatherers: - kind: k8s-discovery @@ -108,6 +324,8 @@ defaults: apiVersion: v1 data: config.yaml: |- + cluster_name: "" + cluster_description: "" period: "12h0m0s" data-gatherers: - kind: k8s-discovery diff --git a/deploy/charts/disco-agent/tests/configmap_test.yaml b/deploy/charts/disco-agent/tests/configmap_test.yaml index 845ae292..59c81576 100644 --- a/deploy/charts/disco-agent/tests/configmap_test.yaml +++ b/deploy/charts/disco-agent/tests/configmap_test.yaml @@ -14,3 +14,20 @@ tests: config.period: 1m asserts: - matchSnapshot: {} + + - it: custom-cluster-name + set: + config.clusterName: "cluster-1 region-1 cloud-1 " + asserts: + - matchSnapshot: {} + + - it: custom-cluster-description + set: + config.clusterDescription: | + A cloud hosted Kubernetes cluster hosting production workloads. + + team: team-1 + email: team-1@example.com + purpose: Production workloads + asserts: + - matchSnapshot: {} diff --git a/deploy/charts/disco-agent/values.schema.json b/deploy/charts/disco-agent/values.schema.json index 6699bd61..35fa2d40 100644 --- a/deploy/charts/disco-agent/values.schema.json +++ b/deploy/charts/disco-agent/values.schema.json @@ -104,6 +104,12 @@ "helm-values.config": { "additionalProperties": false, "properties": { + "clusterDescription": { + "$ref": "#/$defs/helm-values.config.clusterDescription" + }, + "clusterName": { + "$ref": "#/$defs/helm-values.config.clusterName" + }, "excludeAnnotationKeysRegex": { "$ref": "#/$defs/helm-values.config.excludeAnnotationKeysRegex" }, @@ -116,6 +122,16 @@ }, "type": "object" }, + "helm-values.config.clusterDescription": { + "default": "", + "description": "A short description of the cluster where the agent is deployed (optional).\n\nThis description will be associated with the data that the agent uploads to the Discovery and Context service. The description may include contact information such as the email address of the cluster administrator, so that any problems and risks identified by the Discovery and Context service can be communicated to the people responsible for the affected secrets.", + "type": "string" + }, + "helm-values.config.clusterName": { + "default": "", + "description": "A human readable name for the cluster where the agent is deployed (optional).\n\nThis cluster name will be associated with the data that the agent uploads to the Discovery and Context service. If empty (the default), the service account name will be used instead.", + "type": "string" + }, "helm-values.config.excludeAnnotationKeysRegex": { "default": [], "description": "You can configure the agent to exclude some annotations or labels from being pushed . All Kubernetes objects are affected. The objects are still pushed, but the specified annotations and labels are removed before being pushed.\n\nDots is the only character that needs to be escaped in the regex. Use either double quotes with escaped single quotes or unquoted strings for the regex to avoid YAML parsing issues with `\\.`.\n\nExample: excludeAnnotationKeysRegex: ['^kapp\\.k14s\\.io/original.*']", diff --git a/deploy/charts/disco-agent/values.yaml b/deploy/charts/disco-agent/values.yaml index c2ae1bab..be8c71b8 100644 --- a/deploy/charts/disco-agent/values.yaml +++ b/deploy/charts/disco-agent/values.yaml @@ -138,6 +138,22 @@ config: excludeAnnotationKeysRegex: [] excludeLabelKeysRegex: [] + # A human readable name for the cluster where the agent is deployed (optional). + # + # This cluster name will be associated with the data that the agent uploads to + # the Discovery and Context service. If empty (the default), the service + # account name will be used instead. + clusterName: "" + + # A short description of the cluster where the agent is deployed (optional). + # + # This description will be associated with the data that the agent uploads to + # the Discovery and Context service. The description may include contact + # information such as the email address of the cluster administrator, so that + # any problems and risks identified by the Discovery and Context service can + # be communicated to the people responsible for the affected secrets. + clusterDescription: "" + authentication: secretName: agent-credentials diff --git a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt index f74caec3..d510e38a 100644 --- a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt +++ b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt @@ -1,3 +1,11 @@ +{{- if .Values.config.configmap.name }} +You are using a custom configuration in the following ConfigMap: {{ .Values.config.configmap.name | quote }}. + +DEPRECATION: The `cluster_id` configuration field is deprecated. +If your configuration contains `cluster_id`, it will continue to work as a +fallback, but please migrate to `cluster_name` to avoid ambiguity. +{{- end }} + {{- if .Values.authentication.venafiConnection.enabled }} - Check the VenafiConnection exists: "{{ .Values.authentication.venafiConnection.namespace }}/{{ .Values.authentication.venafiConnection.name }}" > kubectl get VenafiConnection -n {{ .Values.authentication.venafiConnection.namespace }} {{ .Values.authentication.venafiConnection.name }} diff --git a/deploy/charts/venafi-kubernetes-agent/templates/configmap.yaml b/deploy/charts/venafi-kubernetes-agent/templates/configmap.yaml index 906013eb..af261e35 100644 --- a/deploy/charts/venafi-kubernetes-agent/templates/configmap.yaml +++ b/deploy/charts/venafi-kubernetes-agent/templates/configmap.yaml @@ -9,7 +9,7 @@ metadata: {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }} data: config.yaml: |- - cluster_id: {{ .Values.config.clusterName | quote }} + cluster_name: {{ .Values.config.clusterName | quote }} cluster_description: {{ .Values.config.clusterDescription | quote }} server: {{ .Values.config.server | quote }} period: {{ .Values.config.period | quote }} diff --git a/deploy/charts/venafi-kubernetes-agent/tests/__snapshot__/configmap_test.yaml.snap b/deploy/charts/venafi-kubernetes-agent/tests/__snapshot__/configmap_test.yaml.snap new file mode 100644 index 00000000..96a51a96 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/tests/__snapshot__/configmap_test.yaml.snap @@ -0,0 +1,1163 @@ +custom-cluster-description: + 1: | + raw: |2 + - Check the credentials Secret exists: "agent-credentials" + > kubectl get secret -n test-ns agent-credentials + - Check the application is running: + > kubectl get pods -n test-ns -l app.kubernetes.io/instance=test + + - Check the application logs for successful connection to the platform: + > kubectl logs -n test-ns -l app.kubernetes.io/instance=test + 2: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "" + cluster_description: "A cloud hosted Kubernetes cluster hosting production workloads.\n\nteam: team-1\nemail: team-1@example.com\npurpose: Production workloads\n" + server: "https://api.venafi.cloud/" + period: "0h1m0s" + venafi-cloud: + uploader_id: "no" + upload_path: "/v1/tlspk/upload/clusterdata" + data-gatherers: + # gather k8s apiserver version information + - kind: "k8s-discovery" + name: "k8s-discovery" + # pods data is used in the pods and application_versions packages + - kind: "k8s-dynamic" + name: "k8s/pods" + config: + resource-type: + resource: pods + version: v1 + - kind: "k8s-dynamic" + name: "k8s/namespaces" + config: + resource-type: + resource: namespaces + version: v1 + # gather services for pod readiness probe rules + - kind: "k8s-dynamic" + name: "k8s/services" + config: + resource-type: + resource: services + version: v1 + # gather higher level resources to ensure data to determine ownership is present + - kind: "k8s-dynamic" + name: "k8s/deployments" + config: + resource-type: + version: v1 + resource: deployments + group: apps + - kind: "k8s-dynamic" + name: "k8s/statefulsets" + config: + resource-type: + version: v1 + resource: statefulsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/daemonsets" + config: + resource-type: + version: v1 + resource: daemonsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/jobs" + config: + resource-type: + version: v1 + resource: jobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/cronjobs" + config: + resource-type: + version: v1 + resource: cronjobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/ingresses" + config: + resource-type: + group: networking.k8s.io + version: v1 + resource: ingresses + - kind: "k8s-dynamic" + name: "k8s/secrets" + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/service-account-token + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=kubernetes.io/basic-auth + - type!=kubernetes.io/ssh-auth + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: "k8s-dynamic" + name: "k8s/certificates" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificates + - kind: "k8s-dynamic" + name: "k8s/certificaterequests" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificaterequests + - kind: "k8s-dynamic" + name: "k8s/issuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/clusterissuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasclusterissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasclusterissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaissuer" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaclusterissuers" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaclusterissuers + - kind: "k8s-dynamic" + name: "k8s/mutatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: mutatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/validatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: validatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/gateways" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: gateways + - kind: "k8s-dynamic" + name: "k8s/virtualservices" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: virtualservices + - kind: "k8s-dynamic" + name: "k8s/routes" + config: + resource-type: + version: v1 + group: route.openshift.io + resource: routes + - kind: "k8s-dynamic" + name: "k8s/venaficonnections" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficonnections + - kind: "k8s-dynamic" + name: "k8s/venaficlusterissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficlusterissuers + - kind: "k8s-dynamic" + name: "k8s/venafiissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venafiissuers + - kind: "k8s-dynamic" + name: "k8s/fireflyissuers" + config: + resource-type: + group: firefly.venafi.com + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/stepissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepissuers + - kind: "k8s-dynamic" + name: "k8s/stepclusterissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepclusterissuers + - kind: "k8s-dynamic" + name: "k8s/originissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: originissuers + - kind: "k8s-dynamic" + name: "k8s/clusteroriginissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: clusteroriginissuers + - kind: "k8s-dynamic" + name: "k8s/freeipaissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/freeipaclusterissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaclusterissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: clusterissuers + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: venafi-kubernetes-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: venafi-kubernetes-agent-0.0.0 + name: agent-config + namespace: test-ns +custom-cluster-name: + 1: | + raw: |2 + - Check the credentials Secret exists: "agent-credentials" + > kubectl get secret -n test-ns agent-credentials + - Check the application is running: + > kubectl get pods -n test-ns -l app.kubernetes.io/instance=test + + - Check the application logs for successful connection to the platform: + > kubectl logs -n test-ns -l app.kubernetes.io/instance=test + 2: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "cluster-1 region-1 cloud-1 " + cluster_description: "" + server: "https://api.venafi.cloud/" + period: "0h1m0s" + venafi-cloud: + uploader_id: "no" + upload_path: "/v1/tlspk/upload/clusterdata" + data-gatherers: + # gather k8s apiserver version information + - kind: "k8s-discovery" + name: "k8s-discovery" + # pods data is used in the pods and application_versions packages + - kind: "k8s-dynamic" + name: "k8s/pods" + config: + resource-type: + resource: pods + version: v1 + - kind: "k8s-dynamic" + name: "k8s/namespaces" + config: + resource-type: + resource: namespaces + version: v1 + # gather services for pod readiness probe rules + - kind: "k8s-dynamic" + name: "k8s/services" + config: + resource-type: + resource: services + version: v1 + # gather higher level resources to ensure data to determine ownership is present + - kind: "k8s-dynamic" + name: "k8s/deployments" + config: + resource-type: + version: v1 + resource: deployments + group: apps + - kind: "k8s-dynamic" + name: "k8s/statefulsets" + config: + resource-type: + version: v1 + resource: statefulsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/daemonsets" + config: + resource-type: + version: v1 + resource: daemonsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/jobs" + config: + resource-type: + version: v1 + resource: jobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/cronjobs" + config: + resource-type: + version: v1 + resource: cronjobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/ingresses" + config: + resource-type: + group: networking.k8s.io + version: v1 + resource: ingresses + - kind: "k8s-dynamic" + name: "k8s/secrets" + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/service-account-token + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=kubernetes.io/basic-auth + - type!=kubernetes.io/ssh-auth + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: "k8s-dynamic" + name: "k8s/certificates" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificates + - kind: "k8s-dynamic" + name: "k8s/certificaterequests" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificaterequests + - kind: "k8s-dynamic" + name: "k8s/issuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/clusterissuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasclusterissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasclusterissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaissuer" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaclusterissuers" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaclusterissuers + - kind: "k8s-dynamic" + name: "k8s/mutatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: mutatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/validatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: validatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/gateways" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: gateways + - kind: "k8s-dynamic" + name: "k8s/virtualservices" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: virtualservices + - kind: "k8s-dynamic" + name: "k8s/routes" + config: + resource-type: + version: v1 + group: route.openshift.io + resource: routes + - kind: "k8s-dynamic" + name: "k8s/venaficonnections" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficonnections + - kind: "k8s-dynamic" + name: "k8s/venaficlusterissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficlusterissuers + - kind: "k8s-dynamic" + name: "k8s/venafiissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venafiissuers + - kind: "k8s-dynamic" + name: "k8s/fireflyissuers" + config: + resource-type: + group: firefly.venafi.com + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/stepissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepissuers + - kind: "k8s-dynamic" + name: "k8s/stepclusterissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepclusterissuers + - kind: "k8s-dynamic" + name: "k8s/originissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: originissuers + - kind: "k8s-dynamic" + name: "k8s/clusteroriginissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: clusteroriginissuers + - kind: "k8s-dynamic" + name: "k8s/freeipaissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/freeipaclusterissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaclusterissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: clusterissuers + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: venafi-kubernetes-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: venafi-kubernetes-agent-0.0.0 + name: agent-config + namespace: test-ns +custom-configmap: + 1: | + |2 + You are using a custom configuration in the following ConfigMap: "agent-custom-config". + + DEPRECATION: The `cluster_id` configuration field is deprecated. + If your configuration contains `cluster_id`, it will continue to work as a + fallback, but please migrate to `cluster_name` to avoid ambiguity. + - Check the credentials Secret exists: "agent-credentials" + > kubectl get secret -n test-ns agent-credentials + - Check the application is running: + > kubectl get pods -n test-ns -l app.kubernetes.io/instance=test + + - Check the application logs for successful connection to the platform: + > kubectl logs -n test-ns -l app.kubernetes.io/instance=test +custom-period: + 1: | + raw: |2 + - Check the credentials Secret exists: "agent-credentials" + > kubectl get secret -n test-ns agent-credentials + - Check the application is running: + > kubectl get pods -n test-ns -l app.kubernetes.io/instance=test + + - Check the application logs for successful connection to the platform: + > kubectl logs -n test-ns -l app.kubernetes.io/instance=test + 2: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "" + cluster_description: "" + server: "https://api.venafi.cloud/" + period: "1m" + venafi-cloud: + uploader_id: "no" + upload_path: "/v1/tlspk/upload/clusterdata" + data-gatherers: + # gather k8s apiserver version information + - kind: "k8s-discovery" + name: "k8s-discovery" + # pods data is used in the pods and application_versions packages + - kind: "k8s-dynamic" + name: "k8s/pods" + config: + resource-type: + resource: pods + version: v1 + - kind: "k8s-dynamic" + name: "k8s/namespaces" + config: + resource-type: + resource: namespaces + version: v1 + # gather services for pod readiness probe rules + - kind: "k8s-dynamic" + name: "k8s/services" + config: + resource-type: + resource: services + version: v1 + # gather higher level resources to ensure data to determine ownership is present + - kind: "k8s-dynamic" + name: "k8s/deployments" + config: + resource-type: + version: v1 + resource: deployments + group: apps + - kind: "k8s-dynamic" + name: "k8s/statefulsets" + config: + resource-type: + version: v1 + resource: statefulsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/daemonsets" + config: + resource-type: + version: v1 + resource: daemonsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/jobs" + config: + resource-type: + version: v1 + resource: jobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/cronjobs" + config: + resource-type: + version: v1 + resource: cronjobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/ingresses" + config: + resource-type: + group: networking.k8s.io + version: v1 + resource: ingresses + - kind: "k8s-dynamic" + name: "k8s/secrets" + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/service-account-token + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=kubernetes.io/basic-auth + - type!=kubernetes.io/ssh-auth + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: "k8s-dynamic" + name: "k8s/certificates" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificates + - kind: "k8s-dynamic" + name: "k8s/certificaterequests" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificaterequests + - kind: "k8s-dynamic" + name: "k8s/issuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/clusterissuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasclusterissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasclusterissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaissuer" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaclusterissuers" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaclusterissuers + - kind: "k8s-dynamic" + name: "k8s/mutatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: mutatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/validatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: validatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/gateways" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: gateways + - kind: "k8s-dynamic" + name: "k8s/virtualservices" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: virtualservices + - kind: "k8s-dynamic" + name: "k8s/routes" + config: + resource-type: + version: v1 + group: route.openshift.io + resource: routes + - kind: "k8s-dynamic" + name: "k8s/venaficonnections" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficonnections + - kind: "k8s-dynamic" + name: "k8s/venaficlusterissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficlusterissuers + - kind: "k8s-dynamic" + name: "k8s/venafiissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venafiissuers + - kind: "k8s-dynamic" + name: "k8s/fireflyissuers" + config: + resource-type: + group: firefly.venafi.com + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/stepissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepissuers + - kind: "k8s-dynamic" + name: "k8s/stepclusterissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepclusterissuers + - kind: "k8s-dynamic" + name: "k8s/originissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: originissuers + - kind: "k8s-dynamic" + name: "k8s/clusteroriginissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: clusteroriginissuers + - kind: "k8s-dynamic" + name: "k8s/freeipaissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/freeipaclusterissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaclusterissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: clusterissuers + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: venafi-kubernetes-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: venafi-kubernetes-agent-0.0.0 + name: agent-config + namespace: test-ns +defaults: + 1: | + raw: |2 + - Check the credentials Secret exists: "agent-credentials" + > kubectl get secret -n test-ns agent-credentials + - Check the application is running: + > kubectl get pods -n test-ns -l app.kubernetes.io/instance=test + + - Check the application logs for successful connection to the platform: + > kubectl logs -n test-ns -l app.kubernetes.io/instance=test + 2: | + apiVersion: v1 + data: + config.yaml: |- + cluster_name: "" + cluster_description: "" + server: "https://api.venafi.cloud/" + period: "0h1m0s" + venafi-cloud: + uploader_id: "no" + upload_path: "/v1/tlspk/upload/clusterdata" + data-gatherers: + # gather k8s apiserver version information + - kind: "k8s-discovery" + name: "k8s-discovery" + # pods data is used in the pods and application_versions packages + - kind: "k8s-dynamic" + name: "k8s/pods" + config: + resource-type: + resource: pods + version: v1 + - kind: "k8s-dynamic" + name: "k8s/namespaces" + config: + resource-type: + resource: namespaces + version: v1 + # gather services for pod readiness probe rules + - kind: "k8s-dynamic" + name: "k8s/services" + config: + resource-type: + resource: services + version: v1 + # gather higher level resources to ensure data to determine ownership is present + - kind: "k8s-dynamic" + name: "k8s/deployments" + config: + resource-type: + version: v1 + resource: deployments + group: apps + - kind: "k8s-dynamic" + name: "k8s/statefulsets" + config: + resource-type: + version: v1 + resource: statefulsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/daemonsets" + config: + resource-type: + version: v1 + resource: daemonsets + group: apps + - kind: "k8s-dynamic" + name: "k8s/jobs" + config: + resource-type: + version: v1 + resource: jobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/cronjobs" + config: + resource-type: + version: v1 + resource: cronjobs + group: batch + - kind: "k8s-dynamic" + name: "k8s/ingresses" + config: + resource-type: + group: networking.k8s.io + version: v1 + resource: ingresses + - kind: "k8s-dynamic" + name: "k8s/secrets" + config: + resource-type: + version: v1 + resource: secrets + field-selectors: + - type!=kubernetes.io/service-account-token + - type!=kubernetes.io/dockercfg + - type!=kubernetes.io/dockerconfigjson + - type!=kubernetes.io/basic-auth + - type!=kubernetes.io/ssh-auth + - type!=bootstrap.kubernetes.io/token + - type!=helm.sh/release.v1 + - kind: "k8s-dynamic" + name: "k8s/certificates" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificates + - kind: "k8s-dynamic" + name: "k8s/certificaterequests" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: certificaterequests + - kind: "k8s-dynamic" + name: "k8s/issuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/clusterissuers" + config: + resource-type: + group: cert-manager.io + version: v1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasissuers + - kind: "k8s-dynamic" + name: "k8s/googlecasclusterissuers" + config: + resource-type: + group: cas-issuer.jetstack.io + version: v1beta1 + resource: googlecasclusterissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaissuer" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaissuers + - kind: "k8s-dynamic" + name: "k8s/awspcaclusterissuers" + config: + resource-type: + group: awspca.cert-manager.io + version: v1beta1 + resource: awspcaclusterissuers + - kind: "k8s-dynamic" + name: "k8s/mutatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: mutatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/validatingwebhookconfigurations" + config: + resource-type: + group: admissionregistration.k8s.io + version: v1 + resource: validatingwebhookconfigurations + - kind: "k8s-dynamic" + name: "k8s/gateways" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: gateways + - kind: "k8s-dynamic" + name: "k8s/virtualservices" + config: + resource-type: + group: networking.istio.io + version: v1alpha3 + resource: virtualservices + - kind: "k8s-dynamic" + name: "k8s/routes" + config: + resource-type: + version: v1 + group: route.openshift.io + resource: routes + - kind: "k8s-dynamic" + name: "k8s/venaficonnections" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficonnections + - kind: "k8s-dynamic" + name: "k8s/venaficlusterissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venaficlusterissuers + - kind: "k8s-dynamic" + name: "k8s/venafiissuers" + config: + resource-type: + group: jetstack.io + version: v1alpha1 + resource: venafiissuers + - kind: "k8s-dynamic" + name: "k8s/fireflyissuers" + config: + resource-type: + group: firefly.venafi.com + version: v1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/stepissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepissuers + - kind: "k8s-dynamic" + name: "k8s/stepclusterissuers" + config: + resource-type: + group: certmanager.step.sm + version: v1beta1 + resource: stepclusterissuers + - kind: "k8s-dynamic" + name: "k8s/originissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: originissuers + - kind: "k8s-dynamic" + name: "k8s/clusteroriginissuers" + config: + resource-type: + group: cert-manager.k8s.cloudflare.com + version: v1 + resource: clusteroriginissuers + - kind: "k8s-dynamic" + name: "k8s/freeipaissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/freeipaclusterissuers" + config: + resource-type: + group: certmanager.freeipa.org + version: v1beta1 + resource: clusterissuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: issuers + - kind: "k8s-dynamic" + name: "k8s/ejbcaclusterissuers" + config: + resource-type: + group: ejbca-issuer.keyfactor.com + version: v1alpha1 + resource: clusterissuers + kind: ConfigMap + metadata: + labels: + app.kubernetes.io/instance: test + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: venafi-kubernetes-agent + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: venafi-kubernetes-agent-0.0.0 + name: agent-config + namespace: test-ns diff --git a/deploy/charts/venafi-kubernetes-agent/tests/configmap_test.yaml b/deploy/charts/venafi-kubernetes-agent/tests/configmap_test.yaml new file mode 100644 index 00000000..4fa49455 --- /dev/null +++ b/deploy/charts/venafi-kubernetes-agent/tests/configmap_test.yaml @@ -0,0 +1,42 @@ +suite: test the contents of the config.yaml +templates: + - configmap.yaml + - NOTES.txt +release: + name: test + namespace: test-ns +tests: + - it: defaults + asserts: + - matchSnapshot: {} + + - it: custom-period + set: + config.period: 1m + asserts: + - matchSnapshot: {} + + - it: custom-cluster-name + set: + config.clusterName: "cluster-1 region-1 cloud-1 " + asserts: + - matchSnapshot: {} + + - it: custom-cluster-description + set: + config.clusterDescription: | + A cloud hosted Kubernetes cluster hosting production workloads. + + team: team-1 + email: team-1@example.com + purpose: Production workloads + asserts: + - matchSnapshot: {} + + - it: custom-configmap + set: + config: + configmap: + name: agent-custom-config + asserts: + - matchSnapshotRaw: {} diff --git a/hack/ark/test-e2e.sh b/hack/ark/test-e2e.sh index 3555f27b..40e147d7 100755 --- a/hack/ark/test-e2e.sh +++ b/hack/ark/test-e2e.sh @@ -74,6 +74,7 @@ helm upgrade agent "oci://${ARK_CHART}@${ARK_CHART_DIGEST}" \ --set pprof.enabled=true \ --set fullnameOverride=disco-agent \ --set "image.digest=${ARK_IMAGE_DIGEST}" \ + --set config.clusterDescription="A temporary cluster for E2E testing. Contact @wallrj-cyberark." \ --set-json "podLabels={\"disco-agent.cyberark.cloud/test-id\": \"${RANDOM}\"}" kubectl rollout status deployments/disco-agent --namespace "${NAMESPACE}" diff --git a/hack/e2e/values.venafi-kubernetes-agent.yaml b/hack/e2e/values.venafi-kubernetes-agent.yaml index 0e5c2120..ef5ca9ed 100644 --- a/hack/e2e/values.venafi-kubernetes-agent.yaml +++ b/hack/e2e/values.venafi-kubernetes-agent.yaml @@ -1,7 +1,7 @@ config: clusterName: venafi-kubernetes-agent-e2e clusterDescription: | - A kind cluster used for testing the venafi-kubernetes-agent. + A cluster used for testing the venafi-kubernetes-agent. excludeAnnotationKeysRegex: ['^kapp\.k14s\.io/original.*'] authentication: diff --git a/internal/cyberark/dataupload/dataupload.go b/internal/cyberark/dataupload/dataupload.go index f73a8c1c..b9ccb5f5 100644 --- a/internal/cyberark/dataupload/dataupload.go +++ b/internal/cyberark/dataupload/dataupload.go @@ -51,6 +51,10 @@ type Snapshot struct { AgentVersion string `json:"agent_version"` // ClusterID is the unique ID of the Kubernetes cluster which this snapshot was taken from. ClusterID string `json:"cluster_id"` + // ClusterName is the name of the Kubernetes cluster which this snapshot was taken from. + ClusterName string `json:"cluster_name"` + // ClusterDescription is an optional description of the Kubernetes cluster which this snapshot was taken from. + ClusterDescription string `json:"cluster_description,omitempty"` // K8SVersion is the version of Kubernetes which the cluster is running. K8SVersion string `json:"k8s_version"` // Secrets is a list of Secret resources in the cluster. Not all Secret diff --git a/pkg/agent/config.go b/pkg/agent/config.go index 34967e19..8cee63b0 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -48,8 +48,12 @@ type Config struct { // API Token modes. OrganizationID string `yaml:"organization_id"` - // ClusterID is the cluster that the agent is scanning. Used in all modes. - ClusterID string `yaml:"cluster_id"` + // ClusterID is the cluster that the agent is scanning. Only used in Jetstack Secure modes. + ClusterID string `yaml:"cluster_id"` + // ClusterName is the name of the Kubernetes cluster where the agent is running. + ClusterName string `yaml:"cluster_name"` + // ClusterDescription is a short description of the Kubernetes cluster where the + // agent is running. ClusterDescription string `yaml:"cluster_description"` DataGatherers []DataGatherer `yaml:"data-gatherers"` VenafiCloud *VenafiCloudConfig `yaml:"venafi-cloud,omitempty"` @@ -340,8 +344,8 @@ const ( MachineHub OutputMode = "MachineHub" ) -// The command-line flags and the config file are combined into this struct by -// ValidateAndCombineConfig. +// The command-line flags and the config file and some environment variables are +// combined into this struct by ValidateAndCombineConfig. type CombinedConfig struct { DataGatherers []DataGatherer Period time.Duration @@ -352,7 +356,7 @@ type CombinedConfig struct { OutputMode OutputMode - // Used by all TLSPK modes. + // Only used in JetstackSecure modes. ClusterID string // Used by JetstackSecureOAuth, JetstackSecureAPIToken, and @@ -364,7 +368,14 @@ type CombinedConfig struct { EndpointPath string // Deprecated. // VenafiCloudKeypair mode only. - UploadPath string + UploadPath string + + // ClusterName is the name of the Kubernetes cluster where the agent is + // running. + ClusterName string + + // ClusterDescription is a short description of the Kubernetes cluster where + // the agent is running. ClusterDescription string // VenafiCloudVenafiConnection mode only. @@ -537,16 +548,29 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) } } - // Validation of `cluster_id` and `organization_id`. + // Validation of `cluster_name`, `cluster_id` and `organization_id`. { - var clusterID string + var clusterName string // Required by venafi cloud modes. Optional for MachineHub mode. + var clusterID string // Required by the old jetstack-secure mode deprecated for venafi cloud modes. var organizationID string // Only used by the old jetstack-secure mode. switch res.OutputMode { // nolint:exhaustive case VenafiCloudKeypair, VenafiCloudVenafiConnection: - if cfg.ClusterID == "" { - errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.OutputMode)) + // For backwards compatibility, use the agent config's `cluster_id` as + // ClusterName if `cluster_name` is not set. + if cfg.ClusterName == "" && cfg.ClusterID == "" { + errs = multierror.Append(errs, fmt.Errorf("cluster_name or cluster_id is required in %s mode", res.OutputMode)) + } + if cfg.ClusterName != "" && cfg.ClusterID != "" { + log.Info(fmt.Sprintf(`Ignoring the cluster_id field in the config file. This field is not needed in %s mode.`, res.OutputMode)) + } + clusterName = cfg.ClusterName + if clusterName == "" { + log.Info("Using cluster_id as cluster_name for backwards compatibility", "clusterID", cfg.ClusterID) + clusterName = cfg.ClusterID + } + if cfg.OrganizationID != "" { + log.Info(fmt.Sprintf(`Ignoring the organization_id field in the config file. This field is not needed in %s mode.`, res.OutputMode)) } - clusterID = cfg.ClusterID case JetstackSecureOAuth, JetstackSecureAPIToken: if cfg.OrganizationID == "" { errs = multierror.Append(errs, fmt.Errorf("organization_id is required")) @@ -557,15 +581,23 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) organizationID = cfg.OrganizationID clusterID = cfg.ClusterID case MachineHub: - if cfg.ClusterID != "" { - log.Info(fmt.Sprintf(`Ignoring the cluster_id field in the config file. This field is not needed in %s mode.`, res.OutputMode)) + clusterName = cfg.ClusterName + if clusterName == "" { + if arkUsername, found := os.LookupEnv("ARK_USERNAME"); found { + log.Info("Using ARK_USERNAME environment variable as cluster name", "clusterName", arkUsername) + clusterName = arkUsername + } } if cfg.OrganizationID != "" { log.Info(fmt.Sprintf(`Ignoring the organization_id field in the config file. This field is not needed in %s mode.`, res.OutputMode)) } + if cfg.ClusterID != "" { + log.Info(fmt.Sprintf(`Ignoring the cluster_id field in the config file. This field is not needed in %s mode.`, res.OutputMode)) + } } res.OrganizationID = organizationID res.ClusterID = clusterID + res.ClusterName = clusterName res.ClusterDescription = cfg.ClusterDescription } diff --git a/pkg/agent/config_test.go b/pkg/agent/config_test.go index 70951b57..dee90d71 100644 --- a/pkg/agent/config_test.go +++ b/pkg/agent/config_test.go @@ -253,7 +253,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { got, cl, err := ValidateAndCombineConfig(discardLogs(), withConfig(testutil.Undent(` server: "http://localhost:8080" - cluster_id: "the cluster name" + cluster_id: "legacy cluster_id as cluster name" period: 1h data-gatherers: - name: d1 @@ -278,7 +278,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { OutputPath: "/nothome", UploadPath: "/testing/path", OutputMode: VenafiCloudKeypair, - ClusterID: "the cluster name", + ClusterName: "legacy cluster_id as cluster name", BackoffMaxTime: 99 * time.Minute, InstallNS: "venafi", } @@ -463,13 +463,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withConfig(testutil.Undent(` server: https://api.venafi.eu period: 1h - cluster_id: the cluster name + cluster_id: legacy cluster_id as cluster name venafi-cloud: upload_path: /foo/bar `)), withCmdLineFlags("--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", path)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OutputMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OutputMode: VenafiCloudKeypair, ClusterName: "legacy cluster_id as cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) assert.IsType(t, &client.VenafiCloudClient{}, cl) }) @@ -485,13 +485,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withConfig(testutil.Undent(` server: https://api.venafi.eu period: 1h - cluster_id: the cluster name + cluster_id: legacy cluster_id as cluster name venafi-cloud: upload_path: /foo/bar `)), withCmdLineFlags("--venafi-cloud", "--credentials-file", credsPath)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OutputMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OutputMode: VenafiCloudKeypair, ClusterName: "legacy cluster_id as cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) }) t.Run("venafi-cloud-keypair-auth: venafi-cloud.upload_path field is required", func(t *testing.T) { @@ -548,7 +548,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--venafi-cloud", "--credentials-file", credsPath, "--private-key-path", privKeyPath)) require.EqualError(t, err, testutil.Undent(` 1 error occurred: - * cluster_id is required in Venafi Cloud Key Pair Service Account mode + * cluster_name or cluster_id is required in Venafi Cloud Key Pair Service Account mode `)) assert.Equal(t, CombinedConfig{}, got) @@ -557,17 +557,24 @@ func Test_ValidateAndCombineConfig(t *testing.T) { t.Run("venafi-cloud-workload-identity-auth: valid --venafi-connection", func(t *testing.T) { t.Setenv("POD_NAMESPACE", "venafi") t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) - got, cl, err := ValidateAndCombineConfig(discardLogs(), + log, gotLogs := recordLogs(t) + got, cl, err := ValidateAndCombineConfig(log, withConfig(testutil.Undent(` server: http://should-be-ignored period: 1h - cluster_id: the cluster name + cluster_id: legacy cluster_id as cluster name `)), withCmdLineFlags("--venafi-connection", "venafi-components")) require.NoError(t, err) + assert.Equal(t, testutil.Undent(` + INFO Output mode selected venConnName="venafi-components" mode="Venafi Cloud VenafiConnection" reason="--venafi-connection was specified" + INFO ignoring the server field specified in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. + INFO Using cluster_id as cluster_name for backwards compatibility clusterID="legacy cluster_id as cluster name" + INFO Using period from config period="1h0m0s" + `), gotLogs.String()) assert.Equal(t, CombinedConfig{ Period: 1 * time.Hour, - ClusterID: "the cluster name", + ClusterName: "legacy cluster_id as cluster name", OutputMode: VenafiCloudVenafiConnection, VenConnName: "venafi-components", VenConnNS: "venafi", @@ -585,7 +592,8 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withConfig(testutil.Undent(` server: https://api.venafi.eu period: 1h - cluster_id: id + cluster_name: cluster-1 + cluster_id: should-be-ignored-and-logged-as-ignored venafi-cloud: uploader_id: id upload_path: /path @@ -598,6 +606,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { INFO ignoring the server field specified in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.upload_path field in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in Venafi Cloud VenafiConnection mode. + INFO Ignoring the cluster_id field in the config file. This field is not needed in Venafi Cloud VenafiConnection mode. INFO Using period from config period="1h0m0s" `), gotLogs.String()) assert.Equal(t, VenafiCloudVenafiConnection, got.OutputMode) @@ -618,17 +627,39 @@ func Test_ValidateAndCombineConfig(t *testing.T) { assert.Equal(t, VenafiCloudVenafiConnection, got.OutputMode) }) + const arkUsername = "cluster-1-region-1-cloud-1@cyberark.cloud.123456" + t.Run("--machine-hub selects MachineHub mode", func(t *testing.T) { t.Setenv("POD_NAMESPACE", "venafi") t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) t.Setenv("ARK_SUBDOMAIN", "tlspk") - t.Setenv("ARK_USERNAME", "first_last@cyberark.cloud.123456") + t.Setenv("ARK_USERNAME", arkUsername) t.Setenv("ARK_SECRET", "test-secret") got, cl, err := ValidateAndCombineConfig(discardLogs(), withConfig(""), withCmdLineFlags("--period", "1m", "--machine-hub")) require.NoError(t, err) assert.Equal(t, MachineHub, got.OutputMode) + assert.Equal(t, arkUsername, got.ClusterName, + "the ClusterName should default to the ARK_USERNAME value if the cluster_name in the config file is empty") + assert.IsType(t, &client.CyberArkClient{}, cl) + }) + + t.Run("--machine-hub with cluster_name override", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "venafi") + t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) + t.Setenv("ARK_SUBDOMAIN", "tlspk") + t.Setenv("ARK_USERNAME", arkUsername) + t.Setenv("ARK_SECRET", "test-secret") + got, cl, err := ValidateAndCombineConfig(discardLogs(), + withConfig(testutil.Undent(` + cluster_name: override-cluster-name + `)), + withCmdLineFlags("--period", "1m", "--machine-hub")) + require.NoError(t, err) + assert.Equal(t, MachineHub, got.OutputMode) + assert.Equal(t, "override-cluster-name", got.ClusterName, + "the cluster_name in the config file should be used if not empty, even if ARK_USERNAME is set") assert.IsType(t, &client.CyberArkClient{}, cl) }) @@ -815,7 +846,7 @@ func Test_ValidateAndCombineConfig_VenafiConnection(t *testing.T) { _, _, err := ValidateAndCombineConfig(discardLogs(), Config{Server: "http://should-be-ignored", Period: 1 * time.Hour}, AgentCmdFlags{VenConnName: "venafi-components", InstallNS: "venafi"}) - assert.EqualError(t, err, "1 error occurred:\n\t* cluster_id is required in Venafi Cloud VenafiConnection mode\n\n") + assert.EqualError(t, err, "1 error occurred:\n\t* cluster_name or cluster_id is required in Venafi Cloud VenafiConnection mode\n\n") }) t.Run("the server field is ignored when VenafiConnection is used", func(t *testing.T) { @@ -841,10 +872,10 @@ func Test_ValidateAndCombineConfig_VenafiConnection(t *testing.T) { testutil.VenConnStartWatching(ctx, t, cl) testutil.TrustCA(t, cl, cert) - // TODO(mael): the client should keep track of the cluster ID, we + // TODO(mael): the client should keep track of the cluster name, we // shouldn't need to pass it as an option to // PostDataReadingsWithOptions. - err = cl.PostDataReadingsWithOptions(ctx, nil, client.Options{ClusterName: cfg.ClusterID}) + err = cl.PostDataReadingsWithOptions(ctx, nil, client.Options{ClusterName: cfg.ClusterName}) require.NoError(t, err) }) } diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 7bf737c3..4da0fa13 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -405,7 +405,7 @@ func postData(ctx context.Context, config CombinedConfig, preflightClient client log := klog.FromContext(ctx).WithName("postData") ctx = klog.NewContext(ctx, log) err := preflightClient.PostDataReadingsWithOptions(ctx, readings, client.Options{ - ClusterName: config.ClusterID, + ClusterName: config.ClusterName, ClusterDescription: config.ClusterDescription, // orgID and clusterID are not required for Venafi Cloud auth OrgID: config.OrganizationID, diff --git a/pkg/client/client.go b/pkg/client/client.go index c424049b..210243f0 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -18,11 +18,10 @@ type ( // Only used with Jetstack Secure. ClusterID string - // Only used with Venafi Cloud. The convention is to use the agent - // config's `cluster_id` as ClusterName. + // Used for Venafi Cloud and MachineHub mode. ClusterName string - // Only used with Venafi Cloud. + // Used for Venafi Cloud and MachineHub mode. ClusterDescription string } diff --git a/pkg/client/client_cyberark.go b/pkg/client/client_cyberark.go index ae94d93a..a979868e 100644 --- a/pkg/client/client_cyberark.go +++ b/pkg/client/client_cyberark.go @@ -52,9 +52,10 @@ func NewCyberArk(httpClient *http.Client) (*CyberArkClient, error) { // It initializes a data upload client with the configured HTTP client and credentials, // then uploads a snapshot. // The supplied Options are not used by this publisher. -func (o *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, readings []*api.DataReading, _ Options) error { +func (o *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, readings []*api.DataReading, opts Options) error { log := klog.FromContext(ctx) - var snapshot dataupload.Snapshot + snapshot := baseSnapshotFromOptions(opts) + if err := convertDataReadings(defaultExtractorFunctions, readings, &snapshot); err != nil { return fmt.Errorf("while converting data readings: %s", err) } @@ -62,8 +63,6 @@ func (o *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, readin // Minimize the snapshot to reduce size and improve privacy minimizeSnapshot(log.V(logs.Debug), &snapshot) - snapshot.AgentVersion = version.PreflightVersion - cfg, err := o.configLoader() if err != nil { return err @@ -80,6 +79,17 @@ func (o *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, readin return nil } +// baseSnapshotFromOptions creates a base snapshot with common fields from the provided options. +// This includes the cluster name, description, and agent version. +// Other fields like ClusterID and K8SVersion need to be populated separately. +func baseSnapshotFromOptions(opts Options) dataupload.Snapshot { + return dataupload.Snapshot{ + ClusterName: opts.ClusterName, + ClusterDescription: opts.ClusterDescription, + AgentVersion: version.PreflightVersion, + } +} + // extractClusterIDAndServerVersionFromReading converts the opaque data from a DiscoveryData // data reading to allow access to the Kubernetes version fields within. func extractClusterIDAndServerVersionFromReading(reading *api.DataReading, target *dataupload.Snapshot) error { diff --git a/pkg/client/client_cyberark_convertdatareadings_test.go b/pkg/client/client_cyberark_convertdatareadings_test.go index 0a8c79ca..5f98f64d 100644 --- a/pkg/client/client_cyberark_convertdatareadings_test.go +++ b/pkg/client/client_cyberark_convertdatareadings_test.go @@ -23,8 +23,40 @@ import ( "github.com/jetstack/preflight/api" "github.com/jetstack/preflight/internal/cyberark/dataupload" + preflightversion "github.com/jetstack/preflight/pkg/version" ) +// TestBaseSnapshotFromOptions tests the baseSnapshotFromOptions function. +func TestBaseSnapshotFromOptions(t *testing.T) { + type testCase struct { + name string + options Options + want dataupload.Snapshot + } + tests := []testCase{ + { + name: "ClusterName and ClusterDescription are used, OrgID and ClusterID", + options: Options{ + OrgID: "unused-org-id", + ClusterID: "unused-cluster-id", + ClusterName: "some-cluster-name", + ClusterDescription: "some-cluster-description", + }, + want: dataupload.Snapshot{ + ClusterName: "some-cluster-name", + ClusterDescription: "some-cluster-description", + AgentVersion: preflightversion.PreflightVersion, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := baseSnapshotFromOptions(tc.options) + require.Equal(t, tc.want, got) + }) + } +} + // TestExtractServerVersionFromReading tests the extractServerVersionFromReading function. func TestExtractServerVersionFromReading(t *testing.T) { type testCase struct {