From dfabdb64d04f09ff589dff7dd4a721eccd0425ba Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:30:27 +0000 Subject: [PATCH] [Feature] ArangoDB Bootstrap --- Makefile | 9 +- chart/arangodb-bootstrap/Chart.yaml | 5 + .../LICENSE | 2 +- chart/arangodb-bootstrap/README.md | 206 ++++++++++++++++++ chart/arangodb-bootstrap/files/bootstrap.js | 158 ++++++++++++++ chart/arangodb-bootstrap/files/start.sh | 25 +++ .../arangodb-bootstrap/templates/_helpers.tpl | 29 +++ chart/arangodb-bootstrap/templates/cm.yaml | 19 ++ chart/arangodb-bootstrap/templates/job.yaml | 98 +++++++++ .../arangodb-bootstrap/templates/secret.yaml | 33 +++ chart/arangodb-bootstrap/values.yaml | 43 ++++ chart/arangodb-ingress-proxy/Chart.yaml | 4 - chart/arangodb-ingress-proxy/README.md | 20 -- .../templates/NOTES.txt | 3 - .../templates/_helpers.tpl | 15 -- .../templates/configmap.yaml | 46 ---- .../templates/deployment.yaml | 68 ------ .../templates/service.yaml | 23 -- chart/arangodb-ingress-proxy/values.yaml | 3 - pkg/handlers/job/handler.go | 99 +++++++-- pkg/util/k8sutil/pods.go | 36 ++- pkg/util/refs.go | 12 +- 22 files changed, 748 insertions(+), 208 deletions(-) create mode 100644 chart/arangodb-bootstrap/Chart.yaml rename chart/{arangodb-ingress-proxy => arangodb-bootstrap}/LICENSE (91%) create mode 100644 chart/arangodb-bootstrap/README.md create mode 100644 chart/arangodb-bootstrap/files/bootstrap.js create mode 100644 chart/arangodb-bootstrap/files/start.sh create mode 100644 chart/arangodb-bootstrap/templates/_helpers.tpl create mode 100644 chart/arangodb-bootstrap/templates/cm.yaml create mode 100644 chart/arangodb-bootstrap/templates/job.yaml create mode 100644 chart/arangodb-bootstrap/templates/secret.yaml create mode 100644 chart/arangodb-bootstrap/values.yaml delete mode 100644 chart/arangodb-ingress-proxy/Chart.yaml delete mode 100644 chart/arangodb-ingress-proxy/README.md delete mode 100644 chart/arangodb-ingress-proxy/templates/NOTES.txt delete mode 100644 chart/arangodb-ingress-proxy/templates/_helpers.tpl delete mode 100644 chart/arangodb-ingress-proxy/templates/configmap.yaml delete mode 100644 chart/arangodb-ingress-proxy/templates/deployment.yaml delete mode 100644 chart/arangodb-ingress-proxy/templates/service.yaml delete mode 100644 chart/arangodb-ingress-proxy/values.yaml diff --git a/Makefile b/Makefile index ceb52967b..6dd0326bd 100644 --- a/Makefile +++ b/Makefile @@ -244,7 +244,7 @@ SOURCES := $(shell $(SOURCES_QUERY)) NON_EE_SOURCES_QUERY := $(SOURCES_QUERY) ! -name '*.enterprise.go' NON_EE_SOURCES := $(shell $(NON_EE_SOURCES_QUERY)) -YAML_EXCLUDE_DIRS := vendor .gobuild deps tools pkg/generated/clientset pkg/generated/informers pkg/generated/listers chart/kube-arangodb/templates chart/kube-arangodb-crd/templates chart/arangodb-ingress-proxy/templates +YAML_EXCLUDE_DIRS := vendor .gobuild deps tools pkg/generated/clientset pkg/generated/informers pkg/generated/listers chart/kube-arangodb/templates chart/kube-arangodb-crd/templates chart/arangodb-ingress-proxy/templates chart/arangodb-bootstrap/templates YAML_EXCLUDE_FILES := YAML_QUERY := find ./ -type f -name '*.yaml' $(foreach EXCLUDE_DIR,$(YAML_EXCLUDE_DIRS), ! -path "*/$(EXCLUDE_DIR)/*") $(foreach EXCLUDE_FILE,$(YAML_EXCLUDE_FILES), ! -path "*/$(EXCLUDE_FILE)") YAMLS := $(shell $(YAML_QUERY)) @@ -573,6 +573,13 @@ $(eval $(call manifest-generator, all, kube-arangodb, \ --set "operator.features.k8sToK8sClusterSync=true" \ --set "operator.features.backup=true")) +.PHONY: chart-bootstrap +chart-bootstrap: export CHART_NAME := arangodb-bootstrap +chart-bootstrap: helm + @mkdir -p "$(ROOTDIR)/bin/charts" + @$(HELM_PACKAGE_CMD) +manifests: chart-bootstrap + .PHONY: chart-crd chart-crd: export CHART_NAME := kube-arangodb-crd chart-crd: helm diff --git a/chart/arangodb-bootstrap/Chart.yaml b/chart/arangodb-bootstrap/Chart.yaml new file mode 100644 index 000000000..325d9719b --- /dev/null +++ b/chart/arangodb-bootstrap/Chart.yaml @@ -0,0 +1,5 @@ +# do not switch to V2 yet - we still need to support Helm 2 +apiVersion: v1 +name: arangodb-bootstrap +version: 1.2.36 +description: "ArangoDB Kubernetes Bootstrap Job" diff --git a/chart/arangodb-ingress-proxy/LICENSE b/chart/arangodb-bootstrap/LICENSE similarity index 91% rename from chart/arangodb-ingress-proxy/LICENSE rename to chart/arangodb-bootstrap/LICENSE index 79013b689..70f564ca0 100644 --- a/chart/arangodb-ingress-proxy/LICENSE +++ b/chart/arangodb-bootstrap/LICENSE @@ -1,4 +1,4 @@ -Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +Copyright 2024 ArangoDB GmbH, Cologne, Germany Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/chart/arangodb-bootstrap/README.md b/chart/arangodb-bootstrap/README.md new file mode 100644 index 000000000..c29273fdd --- /dev/null +++ b/chart/arangodb-bootstrap/README.md @@ -0,0 +1,206 @@ +# Introduction + +Kubernetes ArangoDB Bootstrap Helper. + +# Chart Details + +Chart will bootstrap ArangoDeployment with predefined Users, Databases, Collections and Permissions. + +# Prerequisites + +ArangoDeployment as the destination. Bootstrap will ensure that ArangoDeployment is Ready before starting deployment + +# Installing the Chart + +Chart can be installed in two methods: +- With all Operators in single Helm Release +- One Helm Release per Operator + +Possible Operators: +- `ArangoDeployment` - enabled by default +- `ArangoDeploymentReplications` - enabled by default +- `ArangoLocalStorage` - disabled by default +- `ArangoBackup` - disabled by default +- `ArangoJob` - disabled by default +- `ArangoClusterSynchronization` - disabled by default + +To install Operators in mode "One per Helm Release" we can use: + +``` +helm install --name arango-deployment kube-arangodb.tar.gz \ + --set operator.features.deployment=true \ + --set operator.features.deploymentReplications=false \ + --set operator.features.storage=false \ + --set operator.features.backup=false \ + --set operator.features.apps=false \ + --set operator.features.k8sToK8sClusterSync=false +``` + + +# Configuration + +### `operator.image` + +Image used for the ArangoDB Operator. + +Default: `arangodb/kube-arangodb:latest` + +### `operator.imagePullPolicy` + +Image pull policy for Operator images. + +Default: `IfNotPresent` + +### `operator.imagePullSecrets` + +List of the Image Pull Secrets for Operator images. + +Default: `[]string` + +### `operator.scope` + +Scope on which Operator will be configured. + +Default: `legacy` + +Supported modes: +- `legacy` - mode with limited cluster scope access +- `namespaced` - mode with namespace access only + +### `operator.service.type` + +Type of the Operator service. + +Default: `ClusterIP` + +### `operator.annotations` + +Annotations passed to the Operator Deployment definition. + +Default: `[]string` + +### `operator.resources.limits.cpu` + +CPU limits for operator pods. + +Default: `1` + +### `operator.resources.limits.memory` + +Memory limits for operator pods. + +Default: `256Mi` + +### `operator.resources.requested.cpu` + +Requested CPI by Operator pods. + +Default: `250m` + +### `operator.resources.requested.memory` + +Requested memory for operator pods. + +Default: `256Mi` + +### `operator.nodeSelector` + +NodeSelector for Deployment pods. + +Default: `{}` + +### `operator.tolerations` + +Tolerations for Deployment pods. + +There is built in configuration (can not be changed): +```yaml +tolerations: +- key: "node.kubernetes.io/unreachable" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 5 +- key: "node.kubernetes.io/not-ready" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 5 +``` + +which can be extended by additional entries e.g.: +```yaml +tolerations: +- key: devops + operator: Exists + effect: NoSchedule +``` +Default (empty): `[]` + +### `operator.securityContext.runAsUser` + +Controls which user ID the containers are run with. + +Default: `1000` + +### `operator.replicaCount` + +Replication count for Operator deployment. + +Default: `2` + +### `operator.updateStrategy` + +Update strategy for operator pod. + +Default: `Recreate` + +### `operator.features.deployment` + +Define if ArangoDeployment Operator should be enabled. + +Default: `true` + +### `operator.features.deploymentReplications` + +Define if ArangoDeploymentReplications Operator should be enabled. + +Default: `true` + +### `operator.features.storage` + +Define if ArangoLocalStorage Operator should be enabled. + +Default: `false` + +### `operator.features.backup` + +Define if ArangoBackup Operator should be enabled. + +Default: `false` + +### `operator.features.apps` + +Define if ArangoJob Operator should be enabled. + +Default: `false` + +### `operator.features.k8sToK8sClusterSync` + +Define if ArangoClusterSynchronization Operator should be enabled. + +Default: `false` + +### `rbac.enabled` + +Define if RBAC should be enabled. + +Default: `true` + +### `operator.architectures` + +List of supported architectures. + +Default: `[]string{"amd64"}` + +# Limitations + +N/A \ No newline at end of file diff --git a/chart/arangodb-bootstrap/files/bootstrap.js b/chart/arangodb-bootstrap/files/bootstrap.js new file mode 100644 index 000000000..77d8e1f61 --- /dev/null +++ b/chart/arangodb-bootstrap/files/bootstrap.js @@ -0,0 +1,158 @@ +// +// DISCLAIMER +// +// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +const users = require('@arangodb/users'); + + +function createUser(user, password) { + if (createUserCond(user, password)) { + console.log("User %s created", user) + } else { + console.log("User %s already exists, skip", user) + } +} + +function createUserCond(user, password) { + try { + users.save(user, password); + return true + } catch (e) { + if (e.code === 409 && e.errorNum === 1702) { + return false + } + throw e + } + +} + +function grantCollection(database, collection, user, privilege) { + if (grantCollectionCond(database, collection, user, privilege)) { + console.log("Database %s/%s granted `%s` for %s", database, collection, privilege, user) + } else { + console.log("Database %s/%s grant for %s already exists, skip", database, collection, user) + } +} + +function grantCollectionCond(database, collection, user, privilege) { + try { + users.grantCollection(user, database, collection, privilege) + return true + } catch (e) { + if (e.code === 409 && e.errorNum === 1207) { + return false + } + throw e + } +} + +function grantDatabase(database, user, privilege) { + if (grantDatabaseCond(database, user, privilege)) { + console.log("Database %s granted `%s` for %s", database, privilege, user) + } else { + console.log("Database %s grant for %s already exists, skip", database, user) + } +} + +function grantDatabaseCond(database, user, privilege) { + try { + users.grantDatabase(user, database, privilege) + return true + } catch (e) { + if (e.code === 409 && e.errorNum === 1207) { + return false + } + throw e + } +} + +function createDatabase(database, options) { + if (createDatabaseCond(database, options)) { + console.log("Database %s created", database) + } else { + console.log("Database %s already exists, skip", database) + } +} + +function createDatabaseCond(database, options) { + db._useDatabase("_system"); + try { + db._createDatabase(database, options); + return true + } catch (e) { + if (e.code === 409 && e.errorNum === 1207) { + return false + } + throw e + } +} + +function createCollection(database, collection, properties, type) { + if (createCollectionCond(database, collection, properties, type)) { + console.log("Collection %s/%s created", database, collection) + } else { + console.log("Collection %s/%s already exists, skip", database, collection) + } +} + +function createCollectionCond(database, collection, properties, type) { + db._useDatabase(database); + try { + db._create(collection, properties, type, { + "waitForSyncReplication": true, + "enforceReplicationFactor": true + }); + return true + } catch (e) { + if (e.code === 409 && e.errorNum === 1207) { + return false + } + throw e + } +} + +console.log("Starting ArangoDB Bootstrap") + +db._version(); +console.log("ArangoDB reachable"); + +console.log("Create Users") +{{- range $k, $v := .Values.users }} +createUser({{ $k | quote }}, process.env.PASSWORD_{{ $k | sha256sum | upper }}); +{{- end }} + +console.log("Create Databases") +{{- range $k, $v := .Values.databases }} +{{- if ne $k "*" }} +createDatabase({{ $k | quote }}, {{ $v.options | default dict | toJson }}); +{{- end }} +{{- range $u, $p := ($v.grants | default dict) }} +grantDatabase({{ $k | quote }}, {{ $u | quote }}, {{ $p | quote }}); +{{- end }} +{{- range $c, $co := ($v.collections | default dict) }} +{{- if and (ne $k "*") (ne $c "*") }} +createCollection({{ $k | quote }}, {{ $c | quote }}, {{ $co.attributes | default dict | toJson }}, {{ $co.type | default "document" | quote }}); +{{- end }} +{{- range $u, $p := ($co.grants | default dict) }} +grantCollection({{ $k | quote }}, {{ $c | quote }}, {{ $u | quote }}, {{ $p | quote }}); +{{- end }} +{{- end }} +{{- end }} + +console.log("Bootstrap completed") \ No newline at end of file diff --git a/chart/arangodb-bootstrap/files/start.sh b/chart/arangodb-bootstrap/files/start.sh new file mode 100644 index 000000000..7cb4539bb --- /dev/null +++ b/chart/arangodb-bootstrap/files/start.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e + +ARANGODB_BIN=${ARANGODB_BIN:-/usr/bin/arangosh} +ARANGODB_URL=${ARANGODB_URL:-127.0.0.1} +ARANGODB_PORT=${ARANGODB_PORT:=8529} + +EXEC="${ARANGODB_BIN}" + +if [[ ! -z "${ARANGODB_JWT}" ]]; then + EXEC="${EXEC} --server.authentication true --server.jwt-secret-keyfile ${ARANGODB_JWT}/token" +else + EXEC="${EXEC} --server.authentication false" +fi + +if [[ ! -z "${ARANGODB_TLS}" ]]; then + EXEC="${EXEC} --server.endpoint http+ssl://${ARANGODB_URL}:${ARANGODB_PORT}" +else + EXEC="${EXEC} --server.endpoint http+tcp://${ARANGODB_URL}:${ARANGODB_PORT}" +fi + +EXEC="${EXEC} ${@}" + +echo "Executing \`${EXEC}\`" + +exec $EXEC diff --git a/chart/arangodb-bootstrap/templates/_helpers.tpl b/chart/arangodb-bootstrap/templates/_helpers.tpl new file mode 100644 index 000000000..5e7f00810 --- /dev/null +++ b/chart/arangodb-bootstrap/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "arangodb-bootstrap.name" -}} +{{- printf "%s" .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name of the release. +*/}} +{{- define "arangodb-bootstrap.releaseName" -}} +{{- printf "%s" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Combine name of the deployment. +*/}} +{{- define "arangodb-bootstrap.fullName" -}} +{{- printf "%s-%s" .Chart.Name .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Get Secret Name +*/}} +{{- define "secret.name" -}} +{{- printf "PASSWORD_%s" (. | sha256sum | upper) -}} +{{- end -}} diff --git a/chart/arangodb-bootstrap/templates/cm.yaml b/chart/arangodb-bootstrap/templates/cm.yaml new file mode 100644 index 000000000..fbcb597e6 --- /dev/null +++ b/chart/arangodb-bootstrap/templates/cm.yaml @@ -0,0 +1,19 @@ +{{- $files := .Files }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "arangodb-bootstrap.fullName" . }} + namespace: {{ .Release.Namespace }} +{{- if .Values.job.annotations }} + annotations: +{{ toYaml .Values.operator.annotations | indent 4 }} +{{- end }} + labels: + app.kubernetes.io/name: {{ template "arangodb-bootstrap.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + release: {{ .Release.Name }} +data: +{{ (tpl (.Files.Glob "files/*").AsConfig . ) | indent 2 }} diff --git a/chart/arangodb-bootstrap/templates/job.yaml b/chart/arangodb-bootstrap/templates/job.yaml new file mode 100644 index 000000000..5a95de4c7 --- /dev/null +++ b/chart/arangodb-bootstrap/templates/job.yaml @@ -0,0 +1,98 @@ +--- + +apiVersion: apps.arangodb.com/v1 +kind: ArangoJob +metadata: + name: {{ template "arangodb-bootstrap.fullName" . }} + namespace: {{ .Release.Namespace }} +{{- if .Values.job.annotations }} + annotations: +{{ toYaml .Values.job.annotations | indent 4 }} +{{- end }} + labels: + app.kubernetes.io/name: {{ template "arangodb-bootstrap.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + release: {{ .Release.Name }} +spec: + arangoDeploymentName: {{ .Values.deployment.name }} + jobTemplate: + backoffLimit: 4 + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "arangodb-bootstrap.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + release: {{ .Release.Name }} +{{- if .Values.job.annotations }} + annotations: +{{ toYaml .Values.job.annotations | indent 16 }} +{{- end }} + spec: +{{- if .Values.job.nodeSelector }} + nodeSelector: +{{ toYaml .Values.job.nodeSelector | indent 16 }} +{{- end }} + hostNetwork: false + hostPID: false + hostIPC: false + securityContext: + runAsNonRoot: true + runAsUser: {{ .Values.job.securityContext.runAsUser }} + containers: + - name: bootstrap + imagePullPolicy: {{ .Values.job.imagePullPolicy }} + image: {{ .Values.job.image }} + args: + - /bin/sh + - /bootstrap/start.sh + - --javascript.execute + - /bootstrap/bootstrap.js + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + volumeMounts: + - name: bootstrap + mountPath: /bootstrap + readOnly: true + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - 'ALL' +{{- if .Values.job.resources }} + resources: +{{ toYaml .Values.job.resources | indent 22 }} +{{- end }} + restartPolicy: Never + volumes: + - name: bootstrap + configMap: + name: "{{ template "arangodb-bootstrap.fullName" . }}" + tolerations: + - key: "node.kubernetes.io/unreachable" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 5 + - key: "node.kubernetes.io/not-ready" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 5 +{{- if .Values.job.tolerations }} +{{ toYaml .Values.job.tolerations | indent 16 }} +{{- end }} \ No newline at end of file diff --git a/chart/arangodb-bootstrap/templates/secret.yaml b/chart/arangodb-bootstrap/templates/secret.yaml new file mode 100644 index 000000000..9a17c901a --- /dev/null +++ b/chart/arangodb-bootstrap/templates/secret.yaml @@ -0,0 +1,33 @@ +{{- $root := . -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "arangodb-bootstrap.fullName" . }} + namespace: {{ .Release.Namespace }} +{{- if .Values.job.annotations }} + annotations: +{{ toYaml .Values.operator.annotations | indent 4 }} +{{- end }} + labels: + app.kubernetes.io/name: {{ template "arangodb-bootstrap.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + release: {{ .Release.Name }} +{{- if .Values.users }} +data: +{{- range $u, $s := .Values.users }} +{{- $key := include "secret.name" $u -}} +{{- if eq $s "" }} +{{- if $root.Release.IsInstall }} + {{ $key }}: {{ randAlphaNum 20 | b64enc }} +{{- else }} + {{ $key }}: {{ index (lookup "v1" "Secret" $root.Release.Namespace (include "arangodb-bootstrap.fullName" $root)).data $key }} +{{- end }} +{{- else }} + {{ $key }}: {{ $s | b64enc | quote }} +{{- end }} +{{- end }} +{{- else }} +data: {} +{{- end }} diff --git a/chart/arangodb-bootstrap/values.yaml b/chart/arangodb-bootstrap/values.yaml new file mode 100644 index 000000000..fde3cb90c --- /dev/null +++ b/chart/arangodb-bootstrap/values.yaml @@ -0,0 +1,43 @@ +deployment: + name: arango +users: + user1: "" +databases: + "*": + grants: + user1: rw + collections: + "*": + grants: + user1: rw + mySuperDB: + options: {} + grants: + user1: rw + collections: + collection1: + grants: + user1: rw + type: edge + attributes: + zz: 55 + mySuperDB2: + options: {} + grants: + user1: rw +job: + image: arangodb/arangodb:3.10.5 + imagePullPolicy: IfNotPresent + imagePullSecrets: [] + annotations: {} + resources: + limits: + cpu: 1 + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + securityContext: + runAsUser: 1000 + nodeSelector: {} + tolerations: [] diff --git a/chart/arangodb-ingress-proxy/Chart.yaml b/chart/arangodb-ingress-proxy/Chart.yaml deleted file mode 100644 index 74d1c8abe..000000000 --- a/chart/arangodb-ingress-proxy/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -description: ArangoDB Ingress Proxy -name: arangodb-ingress-proxy -tillerVersion: '>2.7' -version: 1.0.0 diff --git a/chart/arangodb-ingress-proxy/README.md b/chart/arangodb-ingress-proxy/README.md deleted file mode 100644 index 9b5499e96..000000000 --- a/chart/arangodb-ingress-proxy/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Introduction - -Kubernetes ArangoDB Ingress for custom certificates. - -ArangoDB supports more than only HTTP protocol, so simple Ingress is not enough. - -## Before - -Before Ingress proxy will be installed certificate secret needs to be created: - -``` -kubectl -n create secret tls --cert --key -``` - -## Installation - -To install Ingress: -``` -helm install --name --namespace /chart/arangodb-ingress-proxy --set replicas=2 --set tls=TLS Secret name> --set deployment= -``` \ No newline at end of file diff --git a/chart/arangodb-ingress-proxy/templates/NOTES.txt b/chart/arangodb-ingress-proxy/templates/NOTES.txt deleted file mode 100644 index b99bf1620..000000000 --- a/chart/arangodb-ingress-proxy/templates/NOTES.txt +++ /dev/null @@ -1,3 +0,0 @@ -Your LB is ready! - -Get LoadBalancer IP using `kubectl --namespace "{{ .Release.Namespace }}" get svc "{{ template "arangodb-ingress-proxy.name" . }}" -o jsonpath="{.status.loadBalancer.ingress[0].ip}"` \ No newline at end of file diff --git a/chart/arangodb-ingress-proxy/templates/_helpers.tpl b/chart/arangodb-ingress-proxy/templates/_helpers.tpl deleted file mode 100644 index d35a6ae38..000000000 --- a/chart/arangodb-ingress-proxy/templates/_helpers.tpl +++ /dev/null @@ -1,15 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{/* -Expand the name of the chart. -*/}} -{{- define "arangodb-ingress-proxy.name" -}} -{{- printf "%s" .Chart.Name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Expand the name of the release. -*/}} -{{- define "arangodb-ingress-proxy.releaseName" -}} -{{- printf "%s" .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/chart/arangodb-ingress-proxy/templates/configmap.yaml b/chart/arangodb-ingress-proxy/templates/configmap.yaml deleted file mode 100644 index cfa7b9592..000000000 --- a/chart/arangodb-ingress-proxy/templates/configmap.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "arangodb-ingress-proxy.name" . }} - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} -data: - config: | - user nginx; - worker_processes 1; - - error_log /dev/stdout info; - - pid /var/run/nginx.pid; - - - events { - worker_connections 1024; - } - - stream { - log_format basic '$remote_addr [$time_local] ' - '$protocol $status $bytes_sent $bytes_received ' - '$session_time'; - access_log /dev/stdout basic; - - server { - listen 8529 ssl; - proxy_pass {{ required "Arango Deployment name needs to be provided!" .Values.deployment }}:8529; - - proxy_ssl on; - - ssl_certificate /etc/nginx/local-tls/tls.crt; - ssl_certificate_key /etc/nginx/local-tls/tls.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers HIGH:!aNULL:!MD5; - ssl_session_timeout 4h; - ssl_handshake_timeout 30s; - proxy_timeout 6h; - } - } \ No newline at end of file diff --git a/chart/arangodb-ingress-proxy/templates/deployment.yaml b/chart/arangodb-ingress-proxy/templates/deployment.yaml deleted file mode 100644 index ff40aaa2a..000000000 --- a/chart/arangodb-ingress-proxy/templates/deployment.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "arangodb-ingress-proxy.name" . }} - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} -spec: - replicas: {{ .Values.replicas }} - selector: - matchLabels: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - topologyKey: "kubernetes.io/hostname" - labelSelector: - matchExpressions: - - key: app.kubernetes.io/name - operator: In - values: - - {{ template "arangodb-ingress-proxy.name" . }} - containers: - - name: nginx - imagePullPolicy: {{ .Values.imagePullPolicy }} - image: {{ .Values.image }} - ports: - - name: nginx - containerPort: 8529 - volumeMounts: - - mountPath: /etc/nginx/nginx.conf - name: config - subPath: config - - mountPath: /etc/nginx/local-tls - name: tls - volumes: - - name: config - configMap: - name: {{ template "arangodb-ingress-proxy.name" . }} - - name: tls - secret: - secretName: {{ required "TLS certificate need to be provided!" .Values.tls }} \ No newline at end of file diff --git a/chart/arangodb-ingress-proxy/templates/service.yaml b/chart/arangodb-ingress-proxy/templates/service.yaml deleted file mode 100644 index f11e2c815..000000000 --- a/chart/arangodb-ingress-proxy/templates/service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "arangodb-ingress-proxy.name" . }} - namespace: {{ .Release.Namespace }} - labels: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} -spec: - ports: - - name: server - port: 8529 - protocol: TCP - targetPort: 8529 - selector: - app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - release: {{ .Release.Name }} - type: LoadBalancer \ No newline at end of file diff --git a/chart/arangodb-ingress-proxy/values.yaml b/chart/arangodb-ingress-proxy/values.yaml deleted file mode 100644 index 186e10dd7..000000000 --- a/chart/arangodb-ingress-proxy/values.yaml +++ /dev/null @@ -1,3 +0,0 @@ -replicas: 2 -imagePullPolicy: Always -image: nginx:1.16.1-alpine diff --git a/pkg/handlers/job/handler.go b/pkg/handlers/job/handler.go index 9542d7fb6..59e333e1e 100644 --- a/pkg/handlers/job/handler.go +++ b/pkg/handlers/job/handler.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import ( appsApi "github.com/arangodb/kube-arangodb/pkg/apis/apps/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" shared "github.com/arangodb/kube-arangodb/pkg/apis/shared" + "github.com/arangodb/kube-arangodb/pkg/deployment/pod" arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned" operator "github.com/arangodb/kube-arangodb/pkg/operatorV2" "github.com/arangodb/kube-arangodb/pkg/operatorV2/event" @@ -132,7 +133,7 @@ func (h *handler) prepareK8sJob(job *appsApi.ArangoJob) (*batch.Job, error) { k8sJob := batch.Job{} k8sJob.Name = job.Name k8sJob.Namespace = job.Namespace - k8sJob.Spec = *job.Spec.JobTemplate + job.Spec.JobTemplate.DeepCopyInto(&k8sJob.Spec) k8sJob.Spec.Template.Spec.ServiceAccountName = os.Getenv(constants.EnvArangoJobSAName) k8sJob.SetOwnerReferences(append(job.GetOwnerReferences(), job.AsOwner())) @@ -144,29 +145,95 @@ func (h *handler) prepareK8sJob(job *appsApi.ArangoJob) (*batch.Job, error) { spec := deployment.GetAcceptedSpec() + executable, err := os.Executable() + if err != nil { + logger.Error("reading Operator executable name error %v", err) + return &k8sJob, err + } + + initContainer := k8sutil.ArangodWaiterInitContainer(api.ServerGroupReservedInitContainerNameWait, + deployment.Name, + executable, + h.operator.Image(), + &core.SecurityContext{}) + + k8sJob.Spec.Template.Spec.InitContainers = append(k8sJob.Spec.Template.Spec.InitContainers, initContainer) + if spec.TLS.IsSecure() { - k8sJob.Spec.Template.Spec.Volumes = []core.Volume{ - { - Name: shared.TlsKeyfileVolumeName, - VolumeSource: core.VolumeSource{ - Secret: &core.SecretVolumeSource{ - SecretName: spec.TLS.GetCASecretName(), - }, + // Add Volumes + k8sJob.Spec.Template.Spec.Volumes = append(k8sJob.Spec.Template.Spec.Volumes, core.Volume{ + Name: shared.TlsKeyfileVolumeName, + VolumeSource: core.VolumeSource{ + Secret: &core.SecretVolumeSource{ + SecretName: spec.TLS.GetCASecretName(), }, }, + }, + ) + + // Add VolumeMounts and envs + if err := k8sutil.AppendContainersLists(func(in *core.Container) error { + in.VolumeMounts = append(in.VolumeMounts, core.VolumeMount{ + Name: shared.TlsKeyfileVolumeName, + ReadOnly: true, + MountPath: shared.TLSKeyfileVolumeMountDir, + }) + + in.Env = append(in.Env, core.EnvVar{ + Name: "ARANGODB_TLS", + Value: shared.TLSKeyfileVolumeMountDir, + }) + + return nil + }, k8sJob.Spec.Template.Spec.InitContainers, k8sJob.Spec.Template.Spec.Containers); err != nil { + return nil, err } } - executable, err := os.Executable() - if err != nil { - logger.Error("reading Operator executable name error %v", err) - return &k8sJob, err + if spec.Authentication.IsAuthenticated() { + k8sJob.Spec.Template.Spec.Volumes = append(k8sJob.Spec.Template.Spec.Volumes, core.Volume{ + Name: shared.ClusterJWTSecretVolumeName, + VolumeSource: core.VolumeSource{ + Secret: &core.SecretVolumeSource{ + SecretName: pod.JWTSecretFolder(deployment.GetName()), + }, + }, + }, + ) + + // Add VolumeMounts and envs + if err := k8sutil.AppendContainersLists(func(in *core.Container) error { + in.VolumeMounts = append(in.VolumeMounts, core.VolumeMount{ + Name: shared.ClusterJWTSecretVolumeName, + ReadOnly: true, + MountPath: shared.ClusterJWTSecretVolumeMountDir, + }) + + in.Env = append(in.Env, core.EnvVar{ + Name: "ARANGODB_JWT", + Value: shared.ClusterJWTSecretVolumeMountDir, + }) + + return nil + }, k8sJob.Spec.Template.Spec.InitContainers, k8sJob.Spec.Template.Spec.Containers); err != nil { + return nil, err + } } - initContainer := k8sutil.ArangodWaiterInitContainer(api.ServerGroupReservedInitContainerNameWait, deployment.Name, executable, - h.operator.Image(), spec.TLS.IsSecure(), &core.SecurityContext{}) + // Add envs + if err := k8sutil.AppendContainersLists(func(in *core.Container) error { + in.Env = append(in.Env, core.EnvVar{ + Name: "ARANGODB_URL", + Value: deployment.GetName(), + }, core.EnvVar{ + Name: "ARANGODB_PORT", + Value: fmt.Sprintf("%d", shared.ArangoPort), + }) - k8sJob.Spec.Template.Spec.InitContainers = append(k8sJob.Spec.Template.Spec.InitContainers, initContainer) + return nil + }, k8sJob.Spec.Template.Spec.InitContainers, k8sJob.Spec.Template.Spec.Containers); err != nil { + return nil, err + } return &k8sJob, nil } diff --git a/pkg/util/k8sutil/pods.go b/pkg/util/k8sutil/pods.go index 2795948f8..499a62429 100644 --- a/pkg/util/k8sutil/pods.go +++ b/pkg/util/k8sutil/pods.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -490,7 +490,7 @@ func ArangodInitContainer(name, id, engine, executable, operatorImage string, re } // ArangodWaiterInitContainer creates a container configured to wait for specific ArangoDeployment to be ready -func ArangodWaiterInitContainer(name, deploymentName, executable, operatorImage string, isSecured bool, securityContext *core.SecurityContext) core.Container { +func ArangodWaiterInitContainer(name, deploymentName, executable, operatorImage string, securityContext *core.SecurityContext) core.Container { var command = []string{ executable, "lifecycle", @@ -499,11 +499,7 @@ func ArangodWaiterInitContainer(name, deploymentName, executable, operatorImage deploymentName, } - var volumes []core.VolumeMount - if isSecured { - volumes = append(volumes, TlsKeyfileVolumeMount()) - } - return operatorInitContainer(name, operatorImage, command, securityContext, volumes) + return operatorInitContainer(name, operatorImage, command, securityContext, nil) } // createInitContainer creates operator-specific init container @@ -849,3 +845,29 @@ func CreateDefaultContainerTemplate(image *sharedApi.Image) *sharedApi.Container }, } } + +type AppendContainerFunc func(in *core.Container) error + +func AppendContainersLists(appender AppendContainerFunc, containerLists ...[]core.Container) error { + for _, containers := range containerLists { + if err := AppendContainers(appender, util.PointerList(containers)...); err != nil { + return err + } + } + + return nil +} + +func AppendContainers(appender AppendContainerFunc, containers ...*core.Container) error { + for _, container := range containers { + if err := AppendContainer(appender, container); err != nil { + return err + } + } + + return nil +} + +func AppendContainer(appender AppendContainerFunc, container *core.Container) error { + return appender(container) +} diff --git a/pkg/util/refs.go b/pkg/util/refs.go index e426f2be7..28ee7577b 100644 --- a/pkg/util/refs.go +++ b/pkg/util/refs.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -125,3 +125,13 @@ func And(in ...bool) bool { return len(in) > 0 } + +func PointerList[T interface{}](in []T) []*T { + ret := make([]*T, len(in)) + + for id := range in { + ret[id] = &in[id] + } + + return ret +}