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 <deployment namespace> create secret tls <secret name> --cert <path to cert> --key <path to key>
-```
-
-## Installation
-
-To install Ingress:
-```
-helm install --name <my ingress name> --namespace <deployment namespace> <path to kube-arangodb repository>/chart/arangodb-ingress-proxy --set replicas=2 --set tls=TLS Secret name> --set deployment=<ArangoDeployment name>
-```
\ 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
+}