Skip to content

Commit

Permalink
[super-agent] feat: Create identity and mount secret (#1444)
Browse files Browse the repository at this point in the history
Added from this branch to be mergeable with @paologallinaharbur approval
#1442

Co-authored-by: Paolo Gallina <[email protected]>
  • Loading branch information
gsanchezgavier and paologallinaharbur authored Jul 26, 2024
1 parent 1711e26 commit a2a7a07
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 198 deletions.
6 changes: 3 additions & 3 deletions charts/super-agent/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ dependencies:
version: 2.13.0
- name: super-agent-deployment
repository: ""
version: 0.0.20-beta
version: 0.0.21-beta
- name: common-library
repository: https://helm-charts.newrelic.com
version: 1.2.0
digest: sha256:d25475b5e93bd110455a26233c94ba42fa75556799ad10414a31e0043e446311
generated: "2024-07-24T12:28:38.117429+02:00"
digest: sha256:e6adc107915921837bc843af4426a96f0c6d27a9e5c202204b9a56148b31991e
generated: "2024-07-25T18:12:09.459639+02:00"
4 changes: 2 additions & 2 deletions charts/super-agent/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ name: super-agent
description: Bootstraps New Relic' Super Agent

type: application
version: 0.0.14-beta
version: 0.0.15-beta

dependencies:
- name: flux2
repository: https://fluxcd-community.github.io/helm-charts
version: 2.13.0
condition: flux2.enabled
- name: super-agent-deployment
version: 0.0.20-beta
version: 0.0.21-beta
condition: super-agent-deployment.enabled
# The following dependency is needed as sub-dependency of super-agent-deployment
- name: common-library
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Helm chart to install New Relic Super agent on Kubernetes

type: application

version: 0.0.20-beta
version: 0.0.21-beta

keywords:
- newrelic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ Return the name of the configMap holding the Super Agent's config. Defaults to r
{{- (include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" "local-data" "suffix" "superagent-config" )) -}}
{{- end -}}

{{- /*
Return to which endpoint should the super agent ask to renew its token
*/ -}}
{{- define "newrelic-super-agent.config.endpoints.tokenRenewal" -}}
{{- if include "newrelic.common.nrStaging" . -}}
https://system-identity-oauth.staging-service.newrelic.com/oauth2/token
{{- else if .Values.euEndpoints -}}
https://system-identity-oauth.service.eu.newrelic.com/oauth2/token
{{- else -}}
https://system-identity-oauth.service.newrelic.com/oauth2/token
{{- end -}}
{{- end -}}



{{- /*
Return to which endpoint should the super agent register its system identity
*/ -}}
{{- define "newrelic-super-agent.config.endpoints.systemIdentityRegistration" -}}
{{- if include "newrelic.common.nrStaging" . -}}
https://staging-api.newrelic.com/graphql
{{- else if .Values.euEndpoints -}}
https://api.eu.newrelic.com/graphql
{{- else -}}
https://api.newrelic.com/graphql
{{- end -}}
{{- end -}}



{{- /*
Builds the configuration from config on the values and add more config options like
cluster name, licenses, and custom attributes
Expand All @@ -23,6 +53,18 @@ If you need a list of TODOs, just `grep TODO` on the `values.yaml` and look for
{{- $config := .Values.config.superAgent.content | default dict -}}
{{- $config = mustMergeOverwrite (dict "k8s" (dict "cluster_name" (include "newrelic.common.cluster" .))) $config -}}
{{- $config = mustMergeOverwrite (dict "k8s" (dict "namespace" .Release.Namespace)) $config -}}

{{- if .Values.config.superAgent.content -}}
{{- if .Values.config.superAgent.content.opamp -}}
{{- if .Values.config.auth }}
{{- if .Values.config.auth.enabled -}}
{{- $opamp := (dict "opamp" (dict "auth_config" (dict "token_url" (include "newrelic-super-agent.config.endpoints.tokenRenewal" .) "provider" "local" "private_key_path" "/etc/newrelic-super-agent/keys/from-secret.key"))) -}}
{{- $_ := $opamp | mustMergeOverwrite $config -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{- $config | toYaml -}}
{{- end -}}

Expand All @@ -46,31 +88,25 @@ readOnlyRootFilesystem: true
{{- end -}}
{{- end -}}





{{- /*
Check if authSecret.create is explicitly set to true. If authSecret is not empty and create is not defined, default it to false.
Return .Values.config.auth.organizationId and fails if it does not exists
*/ -}}
{{- define "newrelic-super-agent.shouldCreateAuthSecret" -}}
{{- $authSecret := .Values.authSecret }}
{{- if and (hasKey $authSecret "create") }}
{{- toYaml $authSecret.create -}}
{{- else if not (empty $authSecret) }}
{{- toYaml false -}}
{{- else }}
{{- toYaml false -}}
{{- end }}
{{- define "newrelic-super-agent.auth.organizationId" -}}
{{- if not ((.Values.config).auth).organizationId -}}
{{- fail ".Values.config.auth.organizationId is required." -}}
{{- end -}}
{{- .Values.config.auth.organizationId -}}
{{- end -}}



{{- /*
Check if authSecret.data and auth_key are provided. Fail if not.
Releases with "-auth" suffix.
*/ -}}
{{- define "newrelic-super-agent.authSecret.validateData" -}}
{{- $authSecret := .Values.authSecret }}
{{- if and $authSecret (not (empty $authSecret)) }}
{{- if not $authSecret.data }}
{{- fail "authSecret.data must be provided when authSecret.create is true" }}
{{- end }}
{{- if not $authSecret.data.auth_key }}
{{- fail "auth_key must be provided when authSecret.create is true" }}
{{- end }}
{{- end }}
{{- define "newrelic-super-agent.auth.secret.name" -}}
{{- include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "auth") }}
{{- end -}}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}

env:
{{- if .Values.config.auth }}
{{- if .Values.config.auth.enabled }}
- name: NR_SA_OPAMP__AUTH_CONFIG__CLIENT_ID
valueFrom:
secretKeyRef:
name: {{ include "newrelic-super-agent.auth.secret.name" . }}
key: CLIENT_ID
{{- end }}
{{- end }}
- name: NR_LICENSE_KEY
valueFrom:
secretKeyRef:
Expand Down Expand Up @@ -84,14 +93,15 @@ spec:
- mountPath: /var/lib/newrelic-super-agent
name: var-lib-newrelic-super-agent
readOnly: false
{{- if .Values.config.auth }}
{{- if .Values.config.auth.enabled }}
- name: auth-secret-private-key
mountPath: "/etc/newrelic-super-agent/keys"
{{- end }}
{{- end }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if eq (include "newrelic-super-agent.shouldCreateAuthSecret" .) "true" }}
- name: auth-secret-volume
mountPath: "/etc/newrelic-super-agent"
readOnly: true
{{- end }}

resources:
{{- toYaml .Values.resources | nindent 12 }}
Expand All @@ -104,14 +114,19 @@ spec:
path: config.yaml
- name: var-lib-newrelic-super-agent
emptyDir: {}
{{- if .Values.config.auth }}
{{- if .Values.config.auth.enabled }}
- name: auth-secret-private-key
secret:
secretName: {{ include "newrelic-super-agent.auth.secret.name" . }}
items:
- key: private_key
path: from-secret.key
{{- end }}
{{- end }}
{{- with .Values.extraVolumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if eq (include "newrelic-super-agent.shouldCreateAuthSecret" .) "true" }}
- name: auth-secret-volume
secret:
secretName: {{ .Values.authSecret.name | default .Release.Name }}
{{- end }}

{{- with include "newrelic.common.nodeSelector" . }}
nodeSelector:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{{- if .Values.config.auth -}}
{{- if and .Values.config.auth.enabled .Values.config.auth.secret.create -}}
apiVersion: batch/v1
kind: Job
metadata:
annotations:
helm.sh/hook: pre-install # TODO we cannot enable auth after installation, we should add pre-upgrade and check if the secret exists
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
name: {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "system-identity-installer" ) }}
namespace: {{ .Release.Namespace }}
spec:
ttlSecondsAfterFinished: 120
template:
spec:
restartPolicy: Never
serviceAccountName: {{ include "newrelic.common.serviceAccount.name" . }}-auth
containers:
- name: register-system-identity
image: alpine
command:
- ash
args:
- -c
- |
set -uo pipefail
apk update
apk add curl kubectl jq openssl
TEMPORAL_FOLDER=gen-folder
mkdir $TEMPORAL_FOLDER
openssl genrsa -out "$TEMPORAL_FOLDER/key" 4096
openssl rsa -in "$TEMPORAL_FOLDER/key" -pubout -out "$TEMPORAL_FOLDER/pub"
for RETRY in 1 2 3; do
HTTP_CODE=$(echo '{ "query":
"mutation {
systemIdentityCreate(
name: \"System Identity for Kubernetes cluster '{{ include "newrelic.common.cluster" . }}'\",
organizationId: \"{{ include "newrelic-super-agent.auth.organizationId" . }}\",
publicKey: \"'$(openssl enc -base64 -A -in "$TEMPORAL_FOLDER/pub")'\"
) {
clientId,
name
}
}"
}' | tr -d $'\n' | \
curl \
-s -w "%{http_code}" \
-H "Content-Type: application/json" \
-H "API-Key: {{ .Values.config.auth.userApiKey }}" \
-o "$TEMPORAL_FOLDER/response.json" \
--data @- \
"{{ include "newrelic-super-agent.config.endpoints.systemIdentityRegistration" . }}"
)
if [ $HTTP_CODE -eq 200 ]; then
break
fi
echo "Error creating the new system identity. The API endpoint returned $HTTP_CODE. Retrying ($RETRY/3)..."
sleep 2
done
# Retry mechanism failed. Exiting...
if [ $HTTP_CODE -ne 200 ]; then exit 1; fi
ERROR_MESSAGE=$(jq -r '.errors[0].message // "NOERROR"' "$TEMPORAL_FOLDER/response.json")
if [ "$ERROR_MESSAGE" != "NOERROR" ]; then
echo "Error creating an identity: $ERROR_MESSAGE"
exit 1
fi
kubectl create secret generic --dry-run -o json \
{{ include "newrelic-super-agent.auth.secret.name" . }} \
--from-literal=CLIENT_ID=$(jq -r '.data.systemIdentityCreate.clientId' "$TEMPORAL_FOLDER/response.json") \
--from-file="private_key=$TEMPORAL_FOLDER/key" | \
jq '.metadata.labels |= {{ include "newrelic.common.labels" . | fromYaml | toJson }}' | \
kubectl apply -n "{{ .Release.Namespace }}" -f -
---
{{ if .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
helm.sh/hook: pre-install
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
{{- include "newrelic.common.labels" . | nindent 4 }}
name: {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "auth") }}
namespace: {{ .Release.Namespace }}
rules:
- apiGroups: [ "" ]
resources: ["secrets"]
verbs:
- create
- patch
- update
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
annotations:
helm.sh/hook: pre-install
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
labels:
{{- include "newrelic.common.labels" . | nindent 4 }}
name: {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "auth") }}
namespace: {{ .Release.Namespace }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "newrelic.common.naming.truncateToDNSWithSuffix" (dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "auth") }}
subjects:
- kind: ServiceAccount
name: {{ include "newrelic.common.serviceAccount.name" . }}-auth
namespace: {{ .Release.Namespace }}
{{- end -}}

{{ if include "newrelic.common.serviceAccount.create" . }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
helm.sh/hook: pre-install
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
{{- if include "newrelic.common.serviceAccount.annotations" . }}
{{- include "newrelic.common.serviceAccount.annotations" . | nindent 4 }}
{{- end }}
labels:
{{- include "newrelic.common.labels" . | nindent 4 }}
name: {{ include "newrelic.common.serviceAccount.name" . }}-auth
namespace: {{ .Release.Namespace }}
{{- end -}}
{{- end -}}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{- if .Values.cleanupManagedResources -}}
{{- $uninstallJobName := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "deployment-uninstall-job" ) -}}
{{- $uninstallJobName := include "newrelic.common.naming.truncateToDNSWithSuffix" ( dict "name" (include "newrelic.common.naming.fullname" .) "suffix" "deployment-uninstall" ) -}}
{{- /*
The resources managed by the super-agent and the label selector are hardcoded on the super-agent.
*/ -}}
Expand Down
Loading

0 comments on commit a2a7a07

Please sign in to comment.