From 96cf986f5f849ae699e946474a9acd228b48343a Mon Sep 17 00:00:00 2001 From: Ewout Prangsma Date: Mon, 12 Mar 2018 12:18:50 +0100 Subject: [PATCH] Cleaning up deployment --- Jenkinsfile.groovy | 9 +- Makefile | 95 +++++++++--- README.md | 9 +- docs/user/usage.md | 4 +- examples/setup-rbac.sh | 115 -------------- manifests/.gitignore | 1 + manifests/arango-operator.yaml | 95 ++++++++++++ .../templates}/deployment.yaml | 11 +- manifests/templates/rbac.yaml | 67 +++++++++ scripts/kube_create_operator.sh | 72 --------- tools/manifests/manifest_builder.go | 141 ++++++++++++++++++ 11 files changed, 398 insertions(+), 221 deletions(-) delete mode 100755 examples/setup-rbac.sh create mode 100644 manifests/.gitignore create mode 100644 manifests/arango-operator.yaml rename {examples => manifests/templates}/deployment.yaml (67%) create mode 100644 manifests/templates/rbac.yaml delete mode 100755 scripts/kube_create_operator.sh create mode 100644 tools/manifests/manifest_builder.go diff --git a/Jenkinsfile.groovy b/Jenkinsfile.groovy index 3e383bfd7..040c227da 100644 --- a/Jenkinsfile.groovy +++ b/Jenkinsfile.groovy @@ -27,6 +27,7 @@ pipeline { parameters { booleanParam(name: 'LONG', defaultValue: false, description: 'Execute long running tests') string(name: 'KUBECONFIG', defaultValue: '/home/jenkins/.kube/scw-183a3b', description: 'KUBECONFIG controls which k8s cluster is used', ) + string(name: 'DOCKERNAMESPACE', defaultValue: 'arangodb', description: 'DOCKERNAMESPACE sets the docker registry namespace in which the operator docker image will be pushed', ) string(name: 'TESTNAMESPACE', defaultValue: 'jenkins', description: 'TESTNAMESPACE sets the kubernetes namespace to ru tests in (this must be short!!)', ) string(name: 'ENTERPRISEIMAGE', defaultValue: '', description: 'ENTERPRISEIMAGE sets the docker image used for enterprise tests)', ) } @@ -36,6 +37,7 @@ pipeline { timestamps { withEnv([ "IMAGETAG=${env.GIT_COMMIT}", + "DOCKERNAMESPACE=${params.DOCKERNAMESPACE}", ]) { sh "make" sh "make run-unit-tests" @@ -49,12 +51,12 @@ pipeline { lock("${params.TESTNAMESPACE}-${env.GIT_COMMIT}") { withCredentials([string(credentialsId: 'ENTERPRISEIMAGE', variable: 'DEFAULTENTERPRISEIMAGE')]) { withEnv([ + "DOCKERNAMESPACE=${params.DOCKERNAMESPACE}", "ENTERPRISEIMAGE=${params.ENTERPRISEIMAGE}", "IMAGETAG=${env.GIT_COMMIT}", "KUBECONFIG=${params.KUBECONFIG}", "LONG=${params.LONG ? 1 : 0}", - "PUSHIMAGES=1", - "TESTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", + "DEPLOYMENTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", ]) { sh "make run-tests" } @@ -69,8 +71,9 @@ pipeline { always { timestamps { withEnv([ + "DOCKERNAMESPACE=${params.DOCKERNAMESPACE}", "KUBECONFIG=${params.KUBECONFIG}", - "TESTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", + "DEPLOYMENTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", ]) { sh "make cleanup-tests" } diff --git a/Makefile b/Makefile index 5b1258a5d..751420441 100644 --- a/Makefile +++ b/Makefile @@ -24,14 +24,27 @@ GOVERSION := 1.10.0-alpine PULSAR := $(GOBUILDDIR)/bin/pulsar$(shell go env GOEXE) -ifndef DOCKERNAMESPACE - DOCKERNAMESPACE := arangodb -endif DOCKERFILE := Dockerfile DOCKERTESTFILE := Dockerfile.test +ifndef LOCALONLY + PUSHIMAGE := 1 + IMAGESHA256 := true +else + IMAGESHA256 := false +endif + ifdef IMAGETAG - IMAGESUFFIX := ":$(IMAGETAG)" + IMAGESUFFIX := :$(IMAGETAG) +else + IMAGESUFFIX := :dev +endif + +ifndef MANIFESTPATH + MANIFESTPATH := manifests/arango-operator-dev.yaml +endif +ifndef DEPLOYMENTNAMESPACE + DEPLOYMENTNAMESPACE := default endif ifndef OPERATORIMAGE @@ -51,9 +64,6 @@ TESTBIN := $(BINDIR)/$(TESTBINNAME) RELEASE := $(GOBUILDDIR)/bin/release GHRELEASE := $(GOBUILDDIR)/bin/github-release -ifndef TESTNAMESPACE - TESTNAMESPACE := arangodb-operator-tests -endif TESTLENGTHOPTIONS := -test.short TESTTIMEOUT := 20m ifeq ($(LONG), 1) @@ -66,19 +76,29 @@ endif SOURCES := $(shell find $(SRCDIR) -name '*.go' -not -path './test/*') -.PHONY: all clean deps docker update-vendor update-generated verify-generated - +.PHONY: all all: verify-generated docker # # Tip: Run `eval $(minikube docker-env)` before calling make if you're developing on minikube. # -build: docker +.PHONY: build +build: check-vars docker manifests +.PHONY: clean clean: rm -Rf $(BIN) $(BINDIR) $(GOBUILDDIR) +.PHONY: check-vars +check-vars: +ifndef DOCKERNAMESPACE + @echo "DOCKERNAMESPACE must be set" + @exit 1 +endif + @echo "Using docker namespace: $(DOCKERNAMESPACE)" + +.PHONY: deps deps: @${MAKE} -B -s $(GOBUILDDIR) @@ -93,6 +113,7 @@ $(GOBUILDDIR): @rm -f $(REPODIR) && ln -sf ../../../.. $(REPODIR) GOPATH=$(GOBUILDDIR) $(PULSAR) go flatten -V $(VENDORDIR) +.PHONY: update-vendor update-vendor: @mkdir -p $(GOBUILDDIR) @GOPATH=$(GOBUILDDIR) go get github.com/pulcy/pulsar @@ -118,6 +139,7 @@ update-vendor: @$(PULSAR) go flatten -V $(VENDORDIR) $(VENDORDIR) @${MAKE} -B -s clean +.PHONY: update-generated update-generated: $(GOBUILDDIR) @docker build $(SRCDIR)/tools/codegen --build-arg GOVERSION=$(GOVERSION) -t k8s-codegen docker run \ @@ -135,6 +157,7 @@ update-generated: $(GOBUILDDIR) --go-header-file "./tools/codegen/boilerplate.go.txt" \ $(VERIFYARGS) +.PHONY: verify-generated verify-generated: @${MAKE} -B -s VERIFYARGS=--verify-only update-generated @@ -151,14 +174,26 @@ $(BIN): $(GOBUILDDIR) $(SOURCES) golang:$(GOVERSION) \ go build -installsuffix cgo -ldflags "-X main.projectVersion=$(VERSION) -X main.projectBuild=$(COMMIT)" -o /usr/code/bin/$(BINNAME) $(REPOPATH) -docker: $(BIN) +.PHONY: docker +docker: check-vars $(BIN) docker build -f $(DOCKERFILE) -t $(OPERATORIMAGE) . ifdef PUSHIMAGES docker push $(OPERATORIMAGE) endif +# Manifests + +.PHONY: manifests +manifests: + go run $(ROOTDIR)/tools/manifests/manifest_builder.go \ + --output=$(MANIFESTPATH) \ + --image=$(OPERATORIMAGE) \ + --image-sha256=$(IMAGESHA256) \ + --namespace=$(DEPLOYMENTNAMESPACE) + # Testing +.PHONY: run-unit-tests run-unit-tests: $(GOBUILDDIR) $(SOURCES) docker run \ --rm \ @@ -188,39 +223,47 @@ $(TESTBIN): $(GOBUILDDIR) $(SOURCES) golang:$(GOVERSION) \ go test -c -installsuffix cgo -ldflags "-X main.projectVersion=$(VERSION) -X main.projectBuild=$(COMMIT)" -o /usr/code/bin/$(TESTBINNAME) $(REPOPATH)/tests +.PHONY: docker-test docker-test: $(TESTBIN) docker build --quiet -f $(DOCKERTESTFILE) -t $(TESTIMAGE) . +.PHONY: run-tests run-tests: docker-test ifdef PUSHIMAGES docker push $(OPERATORIMAGE) docker push $(TESTIMAGE) endif - $(ROOTDIR)/scripts/kube_delete_namespace.sh $(TESTNAMESPACE) - kubectl create namespace $(TESTNAMESPACE) - $(ROOTDIR)/examples/setup-rbac.sh --namespace=$(TESTNAMESPACE) - $(ROOTDIR)/scripts/kube_create_operator.sh $(TESTNAMESPACE) $(OPERATORIMAGE) - $(ROOTDIR)/scripts/kube_create_storage.sh $(TESTNAMESPACE) - kubectl --namespace $(TESTNAMESPACE) \ + $(ROOTDIR)/scripts/kube_delete_namespace.sh $(DEPLOYMENTNAMESPACE) + kubectl create namespace $(DEPLOYMENTNAMESPACE) + kubectl apply -f $(MANIFESTPATH) + $(ROOTDIR)/scripts/kube_create_storage.sh $(DEPLOYMENTNAMESPACE) + kubectl --namespace $(DEPLOYMENTNAMESPACE) \ run arangodb-operator-test -i --rm --quiet --restart=Never \ --image=$(TESTIMAGE) \ --env="ENTERPRISEIMAGE=$(ENTERPRISEIMAGE)" \ - --env="TEST_NAMESPACE=$(TESTNAMESPACE)" \ + --env="TEST_NAMESPACE=$(DEPLOYMENTNAMESPACE)" \ -- \ -test.v -test.timeout $(TESTTIMEOUT) $(TESTLENGTHOPTIONS) - kubectl delete namespace $(TESTNAMESPACE) --ignore-not-found --now +ifneq ($(DEPLOYMENTNAMESPACE), default) + kubectl delete namespace $(DEPLOYMENTNAMESPACE) --ignore-not-found --now +endif +.PHONY: cleanup-tests cleanup-tests: - $(ROOTDIR)/scripts/kube_delete_namespace.sh $(TESTNAMESPACE) +ifneq ($(DEPLOYMENTNAMESPACE), default) + $(ROOTDIR)/scripts/kube_delete_namespace.sh $(DEPLOYMENTNAMESPACE) +endif # Release building +.PHONY: docker-push docker-push: docker ifneq ($(DOCKERNAMESPACE), arangodb) docker tag $(OPERATORIMAGE) $(DOCKERNAMESPACE)/arangodb-operator endif docker push $(DOCKERNAMESPACE)/arangodb-operator +.PHONY: docker-push-version docker-push-version: docker docker tag arangodb/arangodb-operator arangodb/arangodb-operator:$(VERSION) docker tag arangodb/arangodb-operator arangodb/arangodb-operator:$(VERSION_MAJOR_MINOR) @@ -237,23 +280,29 @@ $(RELEASE): $(GOBUILDDIR) $(SOURCES) $(GHRELEASE) $(GHRELEASE): $(GOBUILDDIR) GOPATH=$(GOBUILDDIR) go build -o $(GHRELEASE) github.com/aktau/github-release +.PHONY: release-patch release-patch: $(RELEASE) GOPATH=$(GOBUILDDIR) $(RELEASE) -type=patch +.PHONY: release-minor release-minor: $(RELEASE) GOPATH=$(GOBUILDDIR) $(RELEASE) -type=minor +.PHONY: release-major release-major: $(RELEASE) GOPATH=$(GOBUILDDIR) $(RELEASE) -type=major ## Kubernetes utilities +.PHONY: minikube-start minikube-start: minikube start --cpus=4 --memory=6144 +.PHONY: delete-operator delete-operator: - kubectl delete deployment arangodb-operator --ignore-not-found + kubectl delete -f $(MANIFESTPATH) --ignore-not-found -redeploy-operator: delete-operator - USESHA256=1 $(ROOTDIR)/scripts/kube_create_operator.sh default $(OPERATORIMAGE) +.PHONY: redeploy-operator +redeploy-operator: delete-operator manifests + kubectl apply -f $(MANIFESTPATH) kubectl get pods diff --git a/README.md b/README.md index 49c4ca58d..0103d1d1a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,14 @@ "Starter for Kubernetes" -State: Design only +State: In development - [User manual](./docs/user/README.md) - [Design documents](./docs/design/README.md) + +## Building + +```bash +DOCKERNAMESPACE= make +kubectl apply -f manifests/arango-operator-dev.yaml +``` diff --git a/docs/user/usage.md b/docs/user/usage.md index 94563cdb5..e59bf64da 100644 --- a/docs/user/usage.md +++ b/docs/user/usage.md @@ -6,7 +6,7 @@ The ArangoDB operator needs to be installed in your Kubernetes cluster first. To do so, clone this repository and run: ```bash -kubectl create -f examples/deployment.yaml +kubectl create -f manifests/arango-operator.yaml ``` ## Cluster creation @@ -37,5 +37,5 @@ To remove the entire ArangoDB operator, remove all clusters first and then remove the operator by running: ```bash -kubectl delete -f examples/deployment.yaml +kubectl delete -f manifests/arango-operator.yaml ``` diff --git a/examples/setup-rbac.sh b/examples/setup-rbac.sh deleted file mode 100755 index 1994c60c1..000000000 --- a/examples/setup-rbac.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash - -set -e - -ROLE_NAME="${ROLE_NAME:-arangodb-operator}" -ROLE_BINDING_NAME="${ROLE_BINDING_NAME:-arangodb-operator}" -NAMESPACE="${NAMESPACE:-default}" - -function usage { -echo "$(basename "$0") - Create Kubernetes RBAC role and bindings for ArangoDB operator -Usage: $(basename "$0") [options...] -Options: - --role-name=STRING Name of ClusterRole to create - (default=\"arangodb-operator\", environment variable: ROLE_NAME) - --role-binding-name=STRING Name of ClusterRoleBinding to create - (default=\"arangodb-operator\", environment variable: ROLE_BINDING_NAME) - --namespace=STRING namespace to create role and role binding in. Must already exist. - (default=\"default\", environment vairable: NAMESPACE) -" >&2 -} - -function setupRole { -kubectl apply -f - << EOYAML -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: ${ROLE_NAME} -rules: -- apiGroups: - - database.arangodb.com - resources: - - arangodeployments - verbs: - - "*" -- apiGroups: - - storage.arangodb.com - resources: - - arangolocalstorages - verbs: - - "*" -- apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - "*" -- apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumes - - persistentvolumeclaims - - events - - secrets - verbs: - - "*" -- apiGroups: - - apps - resources: - - deployments - - daemonsets - verbs: - - "*" -- apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - "*" -EOYAML -} - -function setupRoleBinding { -kubectl apply -f - << EOYAML -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: ${ROLE_BINDING_NAME} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: ${ROLE_NAME} -subjects: -- kind: ServiceAccount - name: default - namespace: ${NAMESPACE} -EOYAML -} - -for i in "$@"; do - case $i in - --role-name=*) - ROLE_NAME="${i#*=}" - ;; - --role-binding-name=*) - ROLE_BINDING_NAME="${i#*=}" - ;; - --namespace=*) - NAMESPACE="${i#*=}" - ;; - -h|--help) - usage - exit 0 - ;; - *) - usage - exit 1 - ;; - esac -done - -setupRole -setupRoleBinding diff --git a/manifests/.gitignore b/manifests/.gitignore new file mode 100644 index 000000000..163d5c8e5 --- /dev/null +++ b/manifests/.gitignore @@ -0,0 +1 @@ +arango-operator-dev.yaml \ No newline at end of file diff --git a/manifests/arango-operator.yaml b/manifests/arango-operator.yaml new file mode 100644 index 000000000..423325c72 --- /dev/null +++ b/manifests/arango-operator.yaml @@ -0,0 +1,95 @@ +## rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: arango-operator +rules: +- apiGroups: + - database.arangodb.com + resources: + - arangodeployments + verbs: + - "*" +- apiGroups: + - storage.arangodb.com + resources: + - arangolocalstorages + verbs: + - "*" +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - "*" +- apiGroups: + - "" + resources: + - pods + - services + - endpoints + - persistentvolumes + - persistentvolumeclaims + - events + - secrets + verbs: + - "*" +- apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - "*" +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - "*" + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: arango-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: arango-operator +subjects: +- kind: ServiceAccount + name: default + namespace: default + + +--- + +## deployment.yaml + +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: arango-operator +spec: + replicas: 1 + template: + metadata: + labels: + name: arango-operator + spec: + containers: + - name: arangodb-operator + imagePullPolicy: IfNotPresent + image: ewoutp/arangodb-operator@sha256:ed82144ebe0d768c99fe44594fff9da7f70aad750254988e9e4247be3575c0fa + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + diff --git a/examples/deployment.yaml b/manifests/templates/deployment.yaml similarity index 67% rename from examples/deployment.yaml rename to manifests/templates/deployment.yaml index 9450bdee0..fe60e3ef5 100644 --- a/examples/deployment.yaml +++ b/manifests/templates/deployment.yaml @@ -1,18 +1,19 @@ + apiVersion: extensions/v1beta1 kind: Deployment metadata: - name: arangodb-operator + name: {{ .OperatorName }} spec: replicas: 1 template: metadata: labels: - name: arangodb-operator + name: {{ .OperatorName }} spec: containers: - name: arangodb-operator - imagePullPolicy: Always - image: arangodb/arangodb-operator:latest + imagePullPolicy: {{ .ImagePullPolicy }} + image: {{ .OperatorImage }} env: - name: MY_POD_NAMESPACE valueFrom: @@ -21,4 +22,4 @@ spec: - name: MY_POD_NAME valueFrom: fieldRef: - fieldPath: metadata.name \ No newline at end of file + fieldPath: metadata.name diff --git a/manifests/templates/rbac.yaml b/manifests/templates/rbac.yaml new file mode 100644 index 000000000..22d3a22e6 --- /dev/null +++ b/manifests/templates/rbac.yaml @@ -0,0 +1,67 @@ + +{{- if .RBAC -}} +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: {{ .ClusterRoleName }} +rules: +- apiGroups: + - database.arangodb.com + resources: + - arangodeployments + verbs: + - "*" +- apiGroups: + - storage.arangodb.com + resources: + - arangolocalstorages + verbs: + - "*" +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - "*" +- apiGroups: + - "" + resources: + - pods + - services + - endpoints + - persistentvolumes + - persistentvolumeclaims + - events + - secrets + verbs: + - "*" +- apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - "*" +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - "*" + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: {{ .ClusterRoleBindingName }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .ClusterRoleName }} +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Namespace }} + +{{- end -}} \ No newline at end of file diff --git a/scripts/kube_create_operator.sh b/scripts/kube_create_operator.sh deleted file mode 100755 index b5ee33f9b..000000000 --- a/scripts/kube_create_operator.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Create the operator deployment with custom image option - -NS=$1 -IMAGE=$2 -PULLPOLICY="${PULLPOLICY:-IfNotPresent}" - -if [ -z $NS ]; then - echo "Specify a namespace argument" - exit 1 -fi -if [ -z $IMAGE ]; then - echo "Specify an image argument" - exit 1 -fi - -if [ ! -z $USESHA256 ]; then - IMAGE=$(docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE}) -fi - -config=$(cat << EOYAML -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: arangodb-operator -spec: - replicas: 1 - template: - metadata: - labels: - name: arangodb-operator - spec: - containers: - - name: arangodb-operator - imagePullPolicy: ${PULLPOLICY} - image: ${IMAGE} - env: - - name: MY_POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: MY_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - -EOYAML -) -echo "$config" | kubectl --namespace=$NS create -f - || exit 1 - -# Wait until custom resources are available - -while :; do - response=$(kubectl get crd arangodeployments.database.arangodb.com --template="non-empty" --ignore-not-found) - if [ ! -z $response ]; then - break - fi - sleep 1 - echo -n . -done - -while :; do - response=$(kubectl get crd arangolocalstorages.storage.arangodb.com --template="non-empty" --ignore-not-found) - if [ ! -z $response ]; then - break - fi - sleep 1 - echo -n . -done - -echo "Arango Operator deployed" diff --git a/tools/manifests/manifest_builder.go b/tools/manifests/manifest_builder.go new file mode 100644 index 000000000..6eca6afe6 --- /dev/null +++ b/tools/manifests/manifest_builder.go @@ -0,0 +1,141 @@ +// +// DISCLAIMER +// +// Copyright 2018 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 +// +// Author Ewout Prangsma +// + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + + "github.com/spf13/pflag" +) + +var ( + options struct { + OutputFile string + TemplatesDir string + + Namespace string + Image string + ImagePullPolicy string + ImageSHA256 bool + OperatorName string + RBAC bool + } + templateNames = []string{ + "rbac.yaml", + "deployment.yaml", + } +) + +func init() { + pflag.StringVar(&options.OutputFile, "output", "manifests/arango-operator.yaml", "Path of the generated manifest file") + pflag.StringVar(&options.TemplatesDir, "templates-dir", "manifests/templates", "Directory containing manifest templates") + pflag.StringVar(&options.Namespace, "namespace", "default", "Namespace in which the operator will be deployed") + pflag.StringVar(&options.Image, "image", "arangodb/arangodb-operator:latest", "Fully qualified image name of the ArangoDB operator") + pflag.StringVar(&options.ImagePullPolicy, "image-pull-policy", "IfNotPresent", "Pull policy of the ArangoDB operator image") + pflag.BoolVar(&options.ImageSHA256, "image-sha256", true, "Use SHA256 syntax for image") + pflag.StringVar(&options.OperatorName, "operator-name", "arango-operator", "Name of the ArangoDB operator deployment") + pflag.BoolVar(&options.RBAC, "rbac", true, "Use role based access control") + + pflag.Parse() +} + +func main() { + // Check options + if options.OutputFile == "" { + log.Fatal("--output not specified.") + } + if options.Namespace == "" { + log.Fatal("--namespace not specified.") + } + if options.Image == "" { + log.Fatal("--image not specified.") + } + + // Fetch image sha256 + if options.ImageSHA256 { + cmd := exec.Command( + "docker", + "inspect", + "--format={{index .RepoDigests 0}}", + options.Image, + ) + result, err := cmd.CombinedOutput() + if err != nil { + log.Println(string(result)) + log.Fatalf("Failed to fetch image SHA256: %v", err) + } + options.Image = strings.TrimSpace(string(result)) + } + + // Process templates + templateOptions := struct { + Namespace string + OperatorName string + OperatorImage string + ImagePullPolicy string + ClusterRoleName string + ClusterRoleBindingName string + RBAC bool + }{ + Namespace: options.Namespace, + OperatorName: options.OperatorName, + OperatorImage: options.Image, + ImagePullPolicy: options.ImagePullPolicy, + ClusterRoleName: "arango-operator", + ClusterRoleBindingName: "arango-operator", + RBAC: options.RBAC, + } + output := &bytes.Buffer{} + for i, name := range templateNames { + t, err := template.New(name).ParseFiles(filepath.Join(options.TemplatesDir, name)) + if err != nil { + log.Fatalf("Failed to parse template %s: %v", name, err) + } + if i > 0 { + output.WriteString("\n---\n\n") + } + output.WriteString(fmt.Sprintf("## %s\n", name)) + t.Execute(output, templateOptions) + output.WriteString("\n") + } + + // Save output + outputPath, err := filepath.Abs(options.OutputFile) + if err != nil { + log.Fatalf("Failed to get absolute output path: %v\n", err) + } + if err := os.MkdirAll(filepath.Base(outputPath), 0755); err != nil { + log.Fatalf("Failed to create output directory: %v\n", err) + } + if err := ioutil.WriteFile(outputPath, output.Bytes(), 0644); err != nil { + log.Fatalf("Failed to write output file: %v\n", err) + } +}