diff --git a/Makefile b/Makefile index 3088e7c0e..f4f2093a0 100644 --- a/Makefile +++ b/Makefile @@ -40,18 +40,20 @@ else IMAGESUFFIX := :dev endif -ifndef MANIFESTPATH - MANIFESTPATH := manifests/arango-operator-dev.yaml +ifndef MANIFESTSUFFIX + MANIFESTSUFFIX := -dev endif +MANIFESTPATHDEPLOYMENT := manifests/arango-deployment$(MANIFESTSUFFIX).yaml +MANIFESTPATHSTORAGE := manifests/arango-storage$(MANIFESTSUFFIX).yaml ifndef DEPLOYMENTNAMESPACE DEPLOYMENTNAMESPACE := default endif ifndef OPERATORIMAGE - OPERATORIMAGE := $(DOCKERNAMESPACE)/arangodb-operator$(IMAGESUFFIX) + OPERATORIMAGE := $(DOCKERNAMESPACE)/kube-arangodb$(IMAGESUFFIX) endif ifndef TESTIMAGE - TESTIMAGE := $(DOCKERNAMESPACE)/arangodb-operator-test$(IMAGESUFFIX) + TESTIMAGE := $(DOCKERNAMESPACE)/kube-arangodb-test$(IMAGESUFFIX) endif ifndef ENTERPRISEIMAGE ENTERPRISEIMAGE := $(DEFAULTENTERPRISEIMAGE) @@ -189,7 +191,7 @@ endif .PHONY: manifests manifests: $(GOBUILDDIR) GOPATH=$(GOBUILDDIR) go run $(ROOTDIR)/tools/manifests/manifest_builder.go \ - --output=$(MANIFESTPATH) \ + --output-suffix=$(MANIFESTSUFFIX) \ --image=$(OPERATORIMAGE) \ --image-sha256=$(IMAGESHA256) \ --namespace=$(DEPLOYMENTNAMESPACE) @@ -240,15 +242,11 @@ ifneq ($(DEPLOYMENTNAMESPACE), default) $(ROOTDIR)/scripts/kube_delete_namespace.sh $(DEPLOYMENTNAMESPACE) kubectl create namespace $(DEPLOYMENTNAMESPACE) endif - kubectl apply -f $(MANIFESTPATH) + kubectl apply -f manifests/crd.yaml + kubectl apply -f $(MANIFESTPATHSTORAGE) + kubectl apply -f $(MANIFESTPATHDEPLOYMENT) $(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=$(DEPLOYMENTNAMESPACE)" \ - -- \ - -test.v -test.timeout $(TESTTIMEOUT) $(TESTLENGTHOPTIONS) + $(ROOTDIR)/scripts/kube_run_tests.sh $(DEPLOYMENTNAMESPACE) $(TESTIMAGE) "$(ENTERPRISEIMAGE)" $(TESTTIMEOUT) $(TESTLENGTHOPTIONS) ifneq ($(DEPLOYMENTNAMESPACE), default) kubectl delete namespace $(DEPLOYMENTNAMESPACE) --ignore-not-found --now endif @@ -308,9 +306,12 @@ minikube-start: .PHONY: delete-operator delete-operator: - kubectl delete -f $(MANIFESTPATH) --ignore-not-found + kubectl delete -f $(MANIFESTPATHDEPLOYMENT) --ignore-not-found + kubectl delete -f $(MANIFESTPATHSTORAGE) --ignore-not-found .PHONY: redeploy-operator redeploy-operator: delete-operator manifests - kubectl apply -f $(MANIFESTPATH) + kubectl apply -f manifests/crd.yaml + kubectl apply -f $(MANIFESTPATHSTORAGE) + kubectl apply -f $(MANIFESTPATHDEPLOYMENT) kubectl get pods diff --git a/README.md b/README.md index 0103d1d1a..029930e3b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ "Starter for Kubernetes" -State: In development +State: In heavy development. DO NOT USE FOR ANY PRODUCTION LIKE PURPOSE! THINGS WILL CHANGE. - [User manual](./docs/user/README.md) - [Design documents](./docs/design/README.md) @@ -11,5 +11,8 @@ State: In development ```bash DOCKERNAMESPACE= make -kubectl apply -f manifests/arango-operator-dev.yaml +kubectl apply -f manifests/crd.yaml +kubectl apply -f manifests/arango-deployment-dev.yaml +# To use `ArangoLocalStorage`, also run +kubectl apply -f manifests/arango-storage-dev.yaml ``` diff --git a/docs/user/custom_resource.md b/docs/user/custom_resource.md index 06f9b554f..c3fff5797 100644 --- a/docs/user/custom_resource.md +++ b/docs/user/custom_resource.md @@ -142,7 +142,8 @@ and restarting it. This setting specifies the name of a kubernetes `Secret` that contains a standard CA certificate + private key used to sign certificates for individual ArangoDB servers. -The default value is empty. TBD +When no name is specified, it defaults to `-ca`. +To disable authentication, set this value to `None`. If you specify a name of a `Secret` that does not exist, a self-signed CA certificate + key is created and stored in a `Secret` with given name. diff --git a/docs/user/tls.md b/docs/user/tls.md index e320eeaa8..d49ea5281 100644 --- a/docs/user/tls.md +++ b/docs/user/tls.md @@ -1,11 +1,13 @@ # TLS -The ArangoDB operator allows you to create ArangoDB deployments that use +The ArangoDB operator will by default create ArangoDB deployments that use secure TLS connections. It uses a single CA certificate (stored in a Kubernetes secret) and one certificate per ArangoDB server (stored in a Kubernetes secret per server). +To disable TLS, set `spec.tls.caSecretName` to `None`. + ## Install CA certificate If the CA certificate is self-signed, it will not be trusted by browsers, diff --git a/docs/user/usage.md b/docs/user/usage.md index d33d76667..8d3e78752 100644 --- a/docs/user/usage.md +++ b/docs/user/usage.md @@ -6,7 +6,14 @@ The ArangoDB operator needs to be installed in your Kubernetes cluster first. To do so, clone this repository and run: ```bash -kubectl apply -f manifests/arango-operator.yaml +kubectl apply -f manifests/crd.yaml +kubectl apply -f manifests/arango-deployment.yaml +``` + +To use `ArangoLocalStorage`, also run: + +```bash +kubectl apply -f manifests/arango-storage.yaml ``` ## Cluster creation @@ -37,5 +44,7 @@ To remove the entire ArangoDB operator, remove all clusters first and then remove the operator by running: ```bash -kubectl delete -f manifests/arango-operator.yaml +kubectl delete -f manifests/arango-deployment.yaml +# If `ArangoLocalStorage` is installed +kubectl delete -f manifests/arango-storage.yaml ``` diff --git a/examples/simple-cluster-no-tls.yaml b/examples/simple-cluster-no-tls.yaml new file mode 100644 index 000000000..68e000155 --- /dev/null +++ b/examples/simple-cluster-no-tls.yaml @@ -0,0 +1,8 @@ +apiVersion: "database.arangodb.com/v1alpha" +kind: "ArangoDeployment" +metadata: + name: "example-simple-cluster-no-tls" +spec: + mode: cluster + tls: + caSecretName: None diff --git a/examples/simple-cluster-tls.yaml b/examples/simple-cluster-tls.yaml deleted file mode 100644 index 18f97aa56..000000000 --- a/examples/simple-cluster-tls.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: "database.arangodb.com/v1alpha" -kind: "ArangoDeployment" -metadata: - name: "example-simple-cluster-tls" -spec: - mode: cluster - tls: - caSecretName: example-simple-cluster-tls - altNames: ["kube-01", "kube-02", "kube-03"] diff --git a/main.go b/main.go index 259c3b097..a066187a3 100644 --- a/main.go +++ b/main.go @@ -41,8 +41,6 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" v1core "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/leaderelection" - "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/client-go/tools/record" "github.com/arangodb/kube-arangodb/pkg/client" @@ -77,7 +75,11 @@ var ( host string port int } - createCRD bool + operatorOptions struct { + enableDeployment bool // Run deployment operator + enableStorage bool // Run deployment operator + createCRD bool + } ) func init() { @@ -85,7 +87,9 @@ func init() { f.StringVar(&server.host, "server.host", defaultServerHost, "Host to listen on") f.IntVar(&server.port, "server.port", defaultServerPort, "Port to listen on") f.StringVar(&logLevel, "log.level", defaultLogLevel, "Set initial log level") - f.BoolVar(&createCRD, "operator.create-crd", true, "Disable to avoid create the custom resource definition") + f.BoolVar(&operatorOptions.enableDeployment, "operator.deployment", false, "Enable to run the ArangoDeployment operator") + f.BoolVar(&operatorOptions.enableStorage, "operator.storage", false, "Enable to run the ArangoLocalStorage operator") + f.BoolVar(&operatorOptions.createCRD, "operator.create-crd", true, "Disable to avoid create the custom resource definition") } func main() { @@ -107,6 +111,11 @@ func cmdMainRun(cmd *cobra.Command, args []string) { cliLog.Fatal().Err(err).Msg("Failed to initialize log service") } + // Check operating mode + if !operatorOptions.enableDeployment && !operatorOptions.enableStorage { + cliLog.Fatal().Err(err).Msg("Turn on --operator.deployment or --operator.storage or both") + } + // Log version cliLog.Info().Msgf("Starting arangodb-operator, version %s build %s", projectVersion, projectBuild) @@ -126,48 +135,12 @@ func cmdMainRun(cmd *cobra.Command, args []string) { cliLog.Fatal().Err(err).Msg("Failed to get hostname") } - // Create k8s client - kubecli, err := k8sutil.NewKubeClient() - if err != nil { - cliLog.Fatal().Err(err).Msg("Failed to create kubernetes client") - } - //http.HandleFunc(probe.HTTPReadyzEndpoint, probe.ReadyzHandler) http.Handle("/metrics", prometheus.Handler()) listenAddr := net.JoinHostPort(server.host, strconv.Itoa(server.port)) go http.ListenAndServe(listenAddr, nil) - rl, err := resourcelock.New(resourcelock.EndpointsResourceLock, - namespace, - "arangodb-operator", - kubecli.CoreV1(), - resourcelock.ResourceLockConfig{ - Identity: id, - EventRecorder: createRecorder(cliLog, kubecli, name, namespace), - }) - if err != nil { - cliLog.Fatal().Err(err).Msg("Failed to create resource lock") - } - - leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{ - Lock: rl, - LeaseDuration: 15 * time.Second, - RenewDeadline: 10 * time.Second, - RetryPeriod: 2 * time.Second, - Callbacks: leaderelection.LeaderCallbacks{ - OnStartedLeading: func(stop <-chan struct{}) { - run(stop, namespace, name) - }, - OnStoppedLeading: func() { - cliLog.Fatal().Msg("Leader election lost") - }, - }, - }) -} - -// run the operator -func run(stop <-chan struct{}, namespace, name string) { - cfg, deps, err := newOperatorConfigAndDeps(namespace, name) + cfg, deps, err := newOperatorConfigAndDeps(id+"-"+name, namespace, name) if err != nil { cliLog.Fatal().Err(err).Msg("Failed to create operator config & deps") } @@ -178,13 +151,11 @@ func run(stop <-chan struct{}, namespace, name string) { if err != nil { cliLog.Fatal().Err(err).Msg("Failed to create operator") } - if err := o.Start(); err != nil { - cliLog.Fatal().Err(err).Msg("Failed to start operator") - } + o.Run() } // newOperatorConfigAndDeps creates operator config & dependencies. -func newOperatorConfigAndDeps(namespace, name string) (operator.Config, operator.Dependencies, error) { +func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, operator.Dependencies, error) { kubecli, err := k8sutil.NewKubeClient() if err != nil { return operator.Config{}, operator.Dependencies{}, maskAny(err) @@ -203,18 +174,23 @@ func newOperatorConfigAndDeps(namespace, name string) (operator.Config, operator if err != nil { return operator.Config{}, operator.Dependencies{}, maskAny(fmt.Errorf("Failed to created versioned client: %s", err)) } + eventRecorder := createRecorder(cliLog, kubecli, name, namespace) cfg := operator.Config{ - Namespace: namespace, - PodName: name, - ServiceAccount: serviceAccount, - CreateCRD: createCRD, + ID: id, + Namespace: namespace, + PodName: name, + ServiceAccount: serviceAccount, + EnableDeployment: operatorOptions.enableDeployment, + EnableStorage: operatorOptions.enableStorage, + CreateCRD: operatorOptions.createCRD, } deps := operator.Dependencies{ - LogService: logService, - KubeCli: kubecli, - KubeExtCli: kubeExtCli, - CRCli: crCli, + LogService: logService, + KubeCli: kubecli, + KubeExtCli: kubeExtCli, + CRCli: crCli, + EventRecorder: eventRecorder, } return cfg, deps, nil diff --git a/manifests/.gitignore b/manifests/.gitignore index 163d5c8e5..10eae2548 100644 --- a/manifests/.gitignore +++ b/manifests/.gitignore @@ -1 +1,2 @@ -arango-operator-dev.yaml \ No newline at end of file +arango-deployment-dev.yaml +arango-storage-dev.yaml \ No newline at end of file diff --git a/manifests/arango-operator.yaml b/manifests/arango-operator.yaml deleted file mode 100644 index a6326f912..000000000 --- a/manifests/arango-operator.yaml +++ /dev/null @@ -1,95 +0,0 @@ -## 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 - namespace: default -spec: - replicas: 1 - template: - metadata: - labels: - name: arango-operator - spec: - containers: - - name: arangodb-operator - imagePullPolicy: IfNotPresent - image: arangodb/arangodb-operator@sha256:90663ccc4cc71562ec06b7e2ad189762aaa38e922e0c70b3cc12f2afa51bf50c - env: - - name: MY_POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: MY_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - diff --git a/manifests/crd.yaml b/manifests/crd.yaml new file mode 100644 index 000000000..e708946d7 --- /dev/null +++ b/manifests/crd.yaml @@ -0,0 +1,34 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: arangodeployments.database.arangodb.com +spec: + group: database.arangodb.com + names: + kind: ArangoDeployment + listKind: ArangoDeploymentList + plural: arangodeployments + shortNames: + - arangodb + - arango + singular: arangodeployment + scope: Namespaced + version: v1alpha + +--- + +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: arangolocalstorages.storage.arangodb.com +spec: + group: storage.arangodb.com + names: + kind: ArangoLocalStorage + listKind: ArangoLocalStorageList + plural: arangolocalstorages + shortNames: + - arangostorage + singular: arangolocalstorage + scope: Namespaced + version: v1alpha diff --git a/manifests/templates/deployment/deployment.yaml b/manifests/templates/deployment/deployment.yaml new file mode 100644 index 000000000..0d6f96e3f --- /dev/null +++ b/manifests/templates/deployment/deployment.yaml @@ -0,0 +1,28 @@ + +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ .Deployment.OperatorName }} + namespace: {{ .Deployment.Namespace }} +spec: + replicas: 1 + template: + metadata: + labels: + name: {{ .Deployment.OperatorName }} + spec: + containers: + - name: operator + imagePullPolicy: {{ .ImagePullPolicy }} + image: {{ .Image }} + args: + - --operator.deployment + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name diff --git a/manifests/templates/rbac.yaml b/manifests/templates/deployment/rbac.yaml similarity index 73% rename from manifests/templates/rbac.yaml rename to manifests/templates/deployment/rbac.yaml index 22d3a22e6..291aeeb71 100644 --- a/manifests/templates/rbac.yaml +++ b/manifests/templates/deployment/rbac.yaml @@ -1,9 +1,8 @@ - {{- if .RBAC -}} apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: - name: {{ .ClusterRoleName }} + name: {{ .Deployment.ClusterRoleName }} rules: - apiGroups: - database.arangodb.com @@ -11,25 +10,18 @@ rules: - arangodeployments verbs: - "*" -- apiGroups: - - storage.arangodb.com - resources: - - arangolocalstorages - verbs: - - "*" - apiGroups: - apiextensions.k8s.io resources: - customresourcedefinitions verbs: - - "*" + - get - apiGroups: - "" resources: - pods - services - endpoints - - persistentvolumes - persistentvolumeclaims - events - secrets @@ -39,7 +31,6 @@ rules: - apps resources: - deployments - - daemonsets verbs: - "*" - apiGroups: @@ -47,21 +38,22 @@ rules: resources: - storageclasses verbs: - - "*" + - get + - list --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: - name: {{ .ClusterRoleBindingName }} + name: {{ .Deployment.ClusterRoleBindingName }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ .ClusterRoleName }} + name: {{ .Deployment.ClusterRoleName }} subjects: - kind: ServiceAccount name: default - namespace: {{ .Namespace }} + namespace: {{ .Deployment.Namespace }} {{- end -}} \ No newline at end of file diff --git a/manifests/templates/deployment.yaml b/manifests/templates/storage/deployment.yaml similarity index 67% rename from manifests/templates/deployment.yaml rename to manifests/templates/storage/deployment.yaml index 89bc24224..9d94b9ea7 100644 --- a/manifests/templates/deployment.yaml +++ b/manifests/templates/storage/deployment.yaml @@ -2,19 +2,21 @@ apiVersion: extensions/v1beta1 kind: Deployment metadata: - name: {{ .OperatorName }} - namespace: {{ .Namespace }} + name: {{ .Storage.OperatorName }} + namespace: {{ .Storage.Namespace }} spec: replicas: 1 template: metadata: labels: - name: {{ .OperatorName }} + name: {{ .Storage.OperatorName }} spec: containers: - - name: arangodb-operator + - name: operator imagePullPolicy: {{ .ImagePullPolicy }} - image: {{ .OperatorImage }} + image: {{ .Image }} + args: + - --operator.storage env: - name: MY_POD_NAMESPACE valueFrom: diff --git a/manifests/templates/storage/rbac.yaml b/manifests/templates/storage/rbac.yaml new file mode 100644 index 000000000..2feee0b4e --- /dev/null +++ b/manifests/templates/storage/rbac.yaml @@ -0,0 +1,56 @@ + +{{- if .RBAC -}} +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: {{ .Storage.ClusterRoleName }} +rules: +- apiGroups: + - storage.arangodb.com + resources: + - arangolocalstorages + verbs: + - "*" +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get +- apiGroups: + - "" + resources: + - persistentvolumes + - persistentvolumeclaims + - events + verbs: + - "*" +- apiGroups: + - apps + resources: + - daemonsets + verbs: + - "*" +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - "*" + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: {{ .Storage.ClusterRoleBindingName }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Storage.ClusterRoleName }} +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Storage.Namespace }} + +{{- end -}} \ No newline at end of file diff --git a/pkg/apis/deployment/v1alpha/deployment_spec.go b/pkg/apis/deployment/v1alpha/deployment_spec.go index c3a986c75..42da4bdb0 100644 --- a/pkg/apis/deployment/v1alpha/deployment_spec.go +++ b/pkg/apis/deployment/v1alpha/deployment_spec.go @@ -113,7 +113,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) { } s.RocksDB.SetDefaults() s.Authentication.SetDefaults(deploymentName + "-jwt") - s.TLS.SetDefaults("") + s.TLS.SetDefaults(deploymentName + "-ca") s.Sync.SetDefaults(s.Image, s.ImagePullPolicy, deploymentName+"-sync-jwt", deploymentName+"-sync-ca") s.Single.SetDefaults(ServerGroupSingle, s.Mode.HasSingleServers(), s.Mode) s.Agents.SetDefaults(ServerGroupAgents, s.Mode.HasAgents(), s.Mode) diff --git a/pkg/apis/deployment/v1alpha/tls_spec.go b/pkg/apis/deployment/v1alpha/tls_spec.go index 896b28031..47eb2b16e 100644 --- a/pkg/apis/deployment/v1alpha/tls_spec.go +++ b/pkg/apis/deployment/v1alpha/tls_spec.go @@ -42,9 +42,14 @@ type TLSSpec struct { TTL time.Duration `json:"ttl,omitempty"` } +const ( + // CASecretNameDisabled is the value of CASecretName to use for disabling authentication. + CASecretNameDisabled = "None" +) + // IsSecure returns true when a CA secret has been set, false otherwise. func (s TLSSpec) IsSecure() bool { - return s.CASecretName != "" + return s.CASecretName != CASecretNameDisabled } // GetAltNames splits the list of AltNames into DNS names, IP addresses & email addresses. diff --git a/pkg/apis/deployment/v1alpha/tls_spec_test.go b/pkg/apis/deployment/v1alpha/tls_spec_test.go index fcd30bb07..9c2783572 100644 --- a/pkg/apis/deployment/v1alpha/tls_spec_test.go +++ b/pkg/apis/deployment/v1alpha/tls_spec_test.go @@ -43,8 +43,9 @@ func TestTLSSpecValidate(t *testing.T) { } func TestTLSSpecIsSecure(t *testing.T) { - assert.False(t, TLSSpec{CASecretName: ""}.IsSecure()) + assert.True(t, TLSSpec{CASecretName: ""}.IsSecure()) assert.True(t, TLSSpec{CASecretName: "foo"}.IsSecure()) + assert.False(t, TLSSpec{CASecretName: "None"}.IsSecure()) } func TestTLSSpecSetDefaults(t *testing.T) { diff --git a/pkg/deployment/plan_builder_test.go b/pkg/deployment/plan_builder_test.go index 042446cf0..07f796ee5 100644 --- a/pkg/deployment/plan_builder_test.go +++ b/pkg/deployment/plan_builder_test.go @@ -28,6 +28,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" ) @@ -39,10 +40,17 @@ func TestCreatePlanSingleScale(t *testing.T) { Mode: api.DeploymentModeSingle, } spec.SetDefaults("test") + depl := &api.ArangoDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test_depl", + Namespace: "test", + }, + Spec: spec, + } // Test with empty status var status api.DeploymentStatus - newPlan, changed := createPlan(log, nil, spec, status, nil) + newPlan, changed := createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) assert.Len(t, newPlan, 0) // Single mode does not scale @@ -53,7 +61,7 @@ func TestCreatePlanSingleScale(t *testing.T) { PodName: "something", }, } - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) assert.Len(t, newPlan, 0) // Single mode does not scale @@ -68,7 +76,7 @@ func TestCreatePlanSingleScale(t *testing.T) { PodName: "something1", }, } - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) assert.Len(t, newPlan, 0) // Single mode does not scale } @@ -81,10 +89,17 @@ func TestCreatePlanResilientSingleScale(t *testing.T) { } spec.SetDefaults("test") spec.Single.Count = 2 + depl := &api.ArangoDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test_depl", + Namespace: "test", + }, + Spec: spec, + } // Test with empty status var status api.DeploymentStatus - newPlan, changed := createPlan(log, nil, spec, status, nil) + newPlan, changed := createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 2) assert.Equal(t, api.ActionTypeAddMember, newPlan[0].Type) @@ -97,7 +112,7 @@ func TestCreatePlanResilientSingleScale(t *testing.T) { PodName: "something", }, } - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 1) assert.Equal(t, api.ActionTypeAddMember, newPlan[0].Type) @@ -122,7 +137,7 @@ func TestCreatePlanResilientSingleScale(t *testing.T) { PodName: "something4", }, } - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 2) // Note: Downscaling is only down 1 at a time assert.Equal(t, api.ActionTypeShutdownMember, newPlan[0].Type) @@ -138,10 +153,17 @@ func TestCreatePlanClusterScale(t *testing.T) { Mode: api.DeploymentModeCluster, } spec.SetDefaults("test") + depl := &api.ArangoDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test_depl", + Namespace: "test", + }, + Spec: spec, + } // Test with empty status var status api.DeploymentStatus - newPlan, changed := createPlan(log, nil, spec, status, nil) + newPlan, changed := createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 6) // Adding 3 dbservers & 3 coordinators (note: agents do not scale now) assert.Equal(t, api.ActionTypeAddMember, newPlan[0].Type) @@ -174,7 +196,7 @@ func TestCreatePlanClusterScale(t *testing.T) { PodName: "coordinator1", }, } - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 3) assert.Equal(t, api.ActionTypeAddMember, newPlan[0].Type) @@ -211,7 +233,7 @@ func TestCreatePlanClusterScale(t *testing.T) { } spec.DBServers.Count = 1 spec.Coordinators.Count = 1 - newPlan, changed = createPlan(log, nil, spec, status, nil) + newPlan, changed = createPlan(log, depl, nil, spec, status, nil) assert.True(t, changed) require.Len(t, newPlan, 5) // Note: Downscaling is done 1 at a time assert.Equal(t, api.ActionTypeCleanOutMember, newPlan[0].Type) diff --git a/pkg/deployment/pod_creator_agent_args_test.go b/pkg/deployment/pod_creator_agent_args_test.go index 982a8c691..f4e44fd11 100644 --- a/pkg/deployment/pod_creator_agent_args_test.go +++ b/pkg/deployment/pod_creator_agent_args_test.go @@ -55,9 +55,9 @@ func TestCreateArangodArgsAgent(t *testing.T) { []string{ "--agency.activate=true", "--agency.disaster-recovery-id=a1", - "--agency.endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--agency.endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--agency.my-address=tcp://name-agnt-a1.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--agency.my-address=ssl://name-agent-a1.name-int.ns.svc:8529", "--agency.size=3", "--agency.supervision=true", "--database.directory=/data", @@ -65,16 +65,18 @@ func TestCreateArangodArgsAgent(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=false", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) } - // Default+TLS deployment + // Default+TLS disabled deployment { apiObject := &api.ArangoDeployment{ ObjectMeta: metav1.ObjectMeta{ @@ -84,7 +86,7 @@ func TestCreateArangodArgsAgent(t *testing.T) { Spec: api.DeploymentSpec{ Mode: api.DeploymentModeCluster, TLS: api.TLSSpec{ - CASecretName: "test-ca", + CASecretName: "None", }, }, } @@ -99,9 +101,9 @@ func TestCreateArangodArgsAgent(t *testing.T) { []string{ "--agency.activate=true", "--agency.disaster-recovery-id=a1", - "--agency.endpoint=ssl://name-agnt-a2.name-int.ns.svc:8529", - "--agency.endpoint=ssl://name-agnt-a3.name-int.ns.svc:8529", - "--agency.my-address=ssl://name-agnt-a1.name-int.ns.svc:8529", + "--agency.endpoint=tcp://name-agent-a2.name-int.ns.svc:8529", + "--agency.endpoint=tcp://name-agent-a3.name-int.ns.svc:8529", + "--agency.my-address=tcp://name-agent-a1.name-int.ns.svc:8529", "--agency.size=3", "--agency.supervision=true", "--database.directory=/data", @@ -109,12 +111,10 @@ func TestCreateArangodArgsAgent(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=ssl://[::]:8529", + "--server.endpoint=tcp://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=false", "--server.storage-engine=rocksdb", - "--ssl.ecdh-curve=", - "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -144,9 +144,9 @@ func TestCreateArangodArgsAgent(t *testing.T) { []string{ "--agency.activate=true", "--agency.disaster-recovery-id=a1", - "--agency.endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--agency.endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--agency.my-address=tcp://name-agnt-a1.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--agency.my-address=ssl://name-agent-a1.name-int.ns.svc:8529", "--agency.size=3", "--agency.supervision=true", "--database.directory=/data", @@ -154,9 +154,11 @@ func TestCreateArangodArgsAgent(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=false", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.statistics=false", "--server.storage-engine=mmfiles", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -185,9 +187,9 @@ func TestCreateArangodArgsAgent(t *testing.T) { []string{ "--agency.activate=true", "--agency.disaster-recovery-id=a1", - "--agency.endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--agency.endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--agency.my-address=tcp://name-agnt-a1.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--agency.endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--agency.my-address=ssl://name-agent-a1.name-int.ns.svc:8529", "--agency.size=3", "--agency.supervision=true", "--database.directory=/data", @@ -195,10 +197,12 @@ func TestCreateArangodArgsAgent(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=false", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", "--foo1", "--foo2", }, diff --git a/pkg/deployment/pod_creator_coordinator_args_test.go b/pkg/deployment/pod_creator_coordinator_args_test.go index 4426d3109..0ef0ab0e6 100644 --- a/pkg/deployment/pod_creator_coordinator_args_test.go +++ b/pkg/deployment/pod_creator_coordinator_args_test.go @@ -53,52 +53,10 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupCoordinators, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-crdn-id1.name-int.ns.svc:8529", - "--cluster.my-role=COORDINATOR", - "--database.directory=/data", - "--foxx.queues=true", - "--log.level=INFO", - "--log.output=+", - "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", - "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", - "--server.statistics=true", - "--server.storage-engine=rocksdb", - }, - cmdline, - ) - } - - // Default+TLS deployment - { - apiObject := &api.ArangoDeployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "ns", - }, - Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, - TLS: api.TLSSpec{ - CASecretName: "test-ca", - }, - }, - } - apiObject.Spec.SetDefaults("test") - agents := api.MemberStatusList{ - api.MemberStatus{ID: "a1"}, - api.MemberStatus{ID: "a2"}, - api.MemberStatus{ID: "a3"}, - } - cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupCoordinators, agents, "id1") - assert.Equal(t, - []string{ - "--cluster.agency-endpoint=ssl://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=ssl://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=ssl://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=ssl://name-crdn-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-coordinator-id1.name-int.ns.svc:8529", "--cluster.my-role=COORDINATOR", "--database.directory=/data", "--foxx.queues=true", @@ -116,6 +74,48 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { ) } + // Default+TLS disabled deployment + { + apiObject := &api.ArangoDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "ns", + }, + Spec: api.DeploymentSpec{ + Mode: api.DeploymentModeCluster, + TLS: api.TLSSpec{ + CASecretName: "None", + }, + }, + } + apiObject.Spec.SetDefaults("test") + agents := api.MemberStatusList{ + api.MemberStatus{ID: "a1"}, + api.MemberStatus{ID: "a2"}, + api.MemberStatus{ID: "a3"}, + } + cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupCoordinators, agents, "id1") + assert.Equal(t, + []string{ + "--cluster.agency-endpoint=tcp://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=tcp://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=tcp://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=tcp://name-coordinator-id1.name-int.ns.svc:8529", + "--cluster.my-role=COORDINATOR", + "--database.directory=/data", + "--foxx.queues=true", + "--log.level=INFO", + "--log.output=+", + "--server.authentication=true", + "--server.endpoint=tcp://[::]:8529", + "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", + "--server.statistics=true", + "--server.storage-engine=rocksdb", + }, + cmdline, + ) + } + // No authentication { apiObject := &api.ArangoDeployment{ @@ -137,19 +137,21 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupCoordinators, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-crdn-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-coordinator-id1.name-int.ns.svc:8529", "--cluster.my-role=COORDINATOR", "--database.directory=/data", "--foxx.queues=true", "--log.level=INFO", "--log.output=+", "--server.authentication=false", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -177,20 +179,22 @@ func TestCreateArangodArgsCoordinator(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupCoordinators, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-crdn-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-coordinator-id1.name-int.ns.svc:8529", "--cluster.my-role=COORDINATOR", "--database.directory=/data", "--foxx.queues=true", "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=mmfiles", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", "--foo1", "--foo2", }, diff --git a/pkg/deployment/pod_creator_dbserver_args_test.go b/pkg/deployment/pod_creator_dbserver_args_test.go index 7b12bdccc..aafec0b1a 100644 --- a/pkg/deployment/pod_creator_dbserver_args_test.go +++ b/pkg/deployment/pod_creator_dbserver_args_test.go @@ -53,52 +53,10 @@ func TestCreateArangodArgsDBServer(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupDBServers, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-prmr-id1.name-int.ns.svc:8529", - "--cluster.my-role=PRIMARY", - "--database.directory=/data", - "--foxx.queues=false", - "--log.level=INFO", - "--log.output=+", - "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", - "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", - "--server.statistics=true", - "--server.storage-engine=rocksdb", - }, - cmdline, - ) - } - - // Default+TLS deployment - { - apiObject := &api.ArangoDeployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "ns", - }, - Spec: api.DeploymentSpec{ - Mode: api.DeploymentModeCluster, - TLS: api.TLSSpec{ - CASecretName: "test-ca", - }, - }, - } - apiObject.Spec.SetDefaults("test") - agents := api.MemberStatusList{ - api.MemberStatus{ID: "a1"}, - api.MemberStatus{ID: "a2"}, - api.MemberStatus{ID: "a3"}, - } - cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupDBServers, agents, "id1") - assert.Equal(t, - []string{ - "--cluster.agency-endpoint=ssl://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=ssl://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=ssl://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=ssl://name-prmr-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-dbserver-id1.name-int.ns.svc:8529", "--cluster.my-role=PRIMARY", "--database.directory=/data", "--foxx.queues=false", @@ -116,6 +74,48 @@ func TestCreateArangodArgsDBServer(t *testing.T) { ) } + // Default+TLS disabled deployment + { + apiObject := &api.ArangoDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "ns", + }, + Spec: api.DeploymentSpec{ + Mode: api.DeploymentModeCluster, + TLS: api.TLSSpec{ + CASecretName: "None", + }, + }, + } + apiObject.Spec.SetDefaults("test") + agents := api.MemberStatusList{ + api.MemberStatus{ID: "a1"}, + api.MemberStatus{ID: "a2"}, + api.MemberStatus{ID: "a3"}, + } + cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupDBServers, agents, "id1") + assert.Equal(t, + []string{ + "--cluster.agency-endpoint=tcp://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=tcp://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=tcp://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=tcp://name-dbserver-id1.name-int.ns.svc:8529", + "--cluster.my-role=PRIMARY", + "--database.directory=/data", + "--foxx.queues=false", + "--log.level=INFO", + "--log.output=+", + "--server.authentication=true", + "--server.endpoint=tcp://[::]:8529", + "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", + "--server.statistics=true", + "--server.storage-engine=rocksdb", + }, + cmdline, + ) + } + // No authentication { apiObject := &api.ArangoDeployment{ @@ -137,19 +137,21 @@ func TestCreateArangodArgsDBServer(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupDBServers, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-prmr-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-dbserver-id1.name-int.ns.svc:8529", "--cluster.my-role=PRIMARY", "--database.directory=/data", "--foxx.queues=false", "--log.level=INFO", "--log.output=+", "--server.authentication=false", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -177,20 +179,22 @@ func TestCreateArangodArgsDBServer(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupDBServers, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-prmr-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-dbserver-id1.name-int.ns.svc:8529", "--cluster.my-role=PRIMARY", "--database.directory=/data", "--foxx.queues=false", "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=mmfiles", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", "--foo1", "--foo2", }, diff --git a/pkg/deployment/pod_creator_single_args_test.go b/pkg/deployment/pod_creator_single_args_test.go index 1cd78e6e2..de1e89a16 100644 --- a/pkg/deployment/pod_creator_single_args_test.go +++ b/pkg/deployment/pod_creator_single_args_test.go @@ -49,22 +49,24 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) } - // Default+TLS deployment + // Default+TLS disabled deployment { apiObject := &api.ArangoDeployment{ Spec: api.DeploymentSpec{ Mode: api.DeploymentModeSingle, TLS: api.TLSSpec{ - CASecretName: "test-ca", + CASecretName: "None", }, }, } @@ -77,12 +79,10 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=ssl://[::]:8529", + "--server.endpoint=tcp://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=rocksdb", - "--ssl.ecdh-curve=", - "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -105,10 +105,12 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=mmfiles", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -131,9 +133,11 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=false", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) @@ -156,10 +160,12 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.level=INFO", "--log.output=+", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", "--foo1", "--foo2", }, @@ -187,10 +193,10 @@ func TestCreateArangodArgsSingle(t *testing.T) { cmdline := createArangodArgs(apiObject, apiObject.Spec, api.ServerGroupSingle, agents, "id1") assert.Equal(t, []string{ - "--cluster.agency-endpoint=tcp://name-agnt-a1.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a2.name-int.ns.svc:8529", - "--cluster.agency-endpoint=tcp://name-agnt-a3.name-int.ns.svc:8529", - "--cluster.my-address=tcp://name-sngl-id1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a1.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a2.name-int.ns.svc:8529", + "--cluster.agency-endpoint=ssl://name-agent-a3.name-int.ns.svc:8529", + "--cluster.my-address=ssl://name-single-id1.name-int.ns.svc:8529", "--cluster.my-role=SINGLE", "--database.directory=/data", "--foxx.queues=true", @@ -198,10 +204,12 @@ func TestCreateArangodArgsSingle(t *testing.T) { "--log.output=+", "--replication.automatic-failover=true", "--server.authentication=true", - "--server.endpoint=tcp://[::]:8529", + "--server.endpoint=ssl://[::]:8529", "--server.jwt-secret=$(ARANGOD_JWT_SECRET)", "--server.statistics=true", "--server.storage-engine=rocksdb", + "--ssl.ecdh-curve=", + "--ssl.keyfile=/secrets/tls/tls.keyfile", }, cmdline, ) diff --git a/pkg/operator/crd.go b/pkg/operator/crd.go index b92a4bdfe..0b20f3cac 100644 --- a/pkg/operator/crd.go +++ b/pkg/operator/crd.go @@ -23,46 +23,29 @@ package operator import ( - "fmt" - - "github.com/pkg/errors" - deplapi "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" lsapi "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha" "github.com/arangodb/kube-arangodb/pkg/util/crd" ) -// initResourceIfNeeded initializes the custom resource definition when -// instructed to do so by the config. -func (o *Operator) initResourceIfNeeded() error { - if o.Config.CreateCRD { - if err := o.initCRD(); err != nil { - return maskAny(fmt.Errorf("Failed to initialize Custom Resource Definition: %v", err)) - } - } - return nil -} - -// initCRD creates the CustomResourceDefinition and waits for it to be ready. -func (o *Operator) initCRD() error { +// waitForCRD waits for the CustomResourceDefinition (created externally) +// to be ready. +func (o *Operator) waitForCRD(enableDeployment, enableStorage bool) error { log := o.log - log.Debug().Msg("Creating ArangoDeployment CRD") - if err := crd.CreateCRD(o.KubeExtCli, deplapi.SchemeGroupVersion, deplapi.ArangoDeploymentCRDName, deplapi.ArangoDeploymentResourceKind, deplapi.ArangoDeploymentResourcePlural, deplapi.ArangoDeploymentShortNames...); err != nil { - return maskAny(errors.Wrapf(err, "failed to create CRD: %v", err)) - } - log.Debug().Msg("Waiting for ArangoDeployment CRD to be ready") - if err := crd.WaitCRDReady(o.KubeExtCli, deplapi.ArangoDeploymentCRDName); err != nil { - return maskAny(err) + if enableDeployment { + log.Debug().Msg("Waiting for ArangoDeployment CRD to be ready") + if err := crd.WaitCRDReady(o.KubeExtCli, deplapi.ArangoDeploymentCRDName); err != nil { + return maskAny(err) + } } - log.Debug().Msg("Creating ArangoLocalStorage CRD") - if err := crd.CreateCRD(o.KubeExtCli, lsapi.SchemeGroupVersion, lsapi.ArangoLocalStorageCRDName, lsapi.ArangoLocalStorageResourceKind, lsapi.ArangoLocalStorageResourcePlural, lsapi.ArangoLocalStorageShortNames...); err != nil { - return maskAny(errors.Wrapf(err, "failed to create CRD: %v", err)) - } - log.Debug().Msg("Waiting for ArangoLocalStorage CRD to be ready") - if err := crd.WaitCRDReady(o.KubeExtCli, lsapi.ArangoLocalStorageCRDName); err != nil { - return maskAny(err) + if enableStorage { + log.Debug().Msg("Waiting for ArangoLocalStorage CRD to be ready") + if err := crd.WaitCRDReady(o.KubeExtCli, lsapi.ArangoLocalStorageCRDName); err != nil { + return maskAny(err) + } } + return nil } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index ccdb1394a..13d21b504 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -26,9 +26,12 @@ import ( "context" "time" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" kwatch "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/record" deplapi "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha" lsapi "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha" @@ -36,7 +39,6 @@ import ( "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned" "github.com/arangodb/kube-arangodb/pkg/logging" "github.com/arangodb/kube-arangodb/pkg/storage" - "github.com/rs/zerolog" ) const ( @@ -59,17 +61,21 @@ type Operator struct { } type Config struct { - Namespace string - PodName string - ServiceAccount string - CreateCRD bool + ID string + Namespace string + PodName string + ServiceAccount string + EnableDeployment bool + EnableStorage bool + CreateCRD bool } type Dependencies struct { - LogService logging.Service - KubeCli kubernetes.Interface - KubeExtCli apiextensionsclient.Interface - CRCli versioned.Interface + LogService logging.Service + KubeCli kubernetes.Interface + KubeExtCli apiextensionsclient.Interface + CRCli versioned.Interface + EventRecorder record.EventRecorder } // NewOperator instantiates a new operator from given config & dependencies. @@ -84,12 +90,22 @@ func NewOperator(config Config, deps Dependencies) (*Operator, error) { return o, nil } -// Start the operator -func (o *Operator) Start() error { - log := o.log +// Run the operator +func (o *Operator) Run() { + if o.Config.EnableDeployment { + go o.runLeaderElection("arango-deployment-operator", o.onStartDeployment) + } + if o.Config.EnableStorage { + go o.runLeaderElection("arango-storage-operator", o.onStartStorage) + } + // Wait until process terminates + <-context.TODO().Done() +} +// onStartDeployment starts the deployment operator and run till given channel is closed. +func (o *Operator) onStartDeployment(stop <-chan struct{}) { for { - if err := o.initResourceIfNeeded(); err == nil { + if err := o.waitForCRD(true, false); err == nil { break } else { log.Error().Err(err).Msg("Resource initialization failed") @@ -97,23 +113,19 @@ func (o *Operator) Start() error { time.Sleep(initRetryWaitTime) } } - - //probe.SetReady() - o.run() - panic("unreachable") + o.runDeployments(stop) } -// run the operator. -// This registers a listener and waits until the process stops. -func (o *Operator) run() { - log := o.log - - log.Info().Msgf("Running controller in namespace '%s'", o.Config.Namespace) - - go o.runDeployments() - go o.runLocalStorages() - - // Wait till done - ctx := context.TODO() - <-ctx.Done() +// onStartStorage starts the storage operator and run till given channel is closed. +func (o *Operator) onStartStorage(stop <-chan struct{}) { + for { + if err := o.waitForCRD(false, true); err == nil { + break + } else { + log.Error().Err(err).Msg("Resource initialization failed") + log.Info().Msgf("Retrying in %s...", initRetryWaitTime) + time.Sleep(initRetryWaitTime) + } + } + o.runLocalStorages(stop) } diff --git a/pkg/operator/operator_deployment.go b/pkg/operator/operator_deployment.go index 542d98341..a56dda4f6 100644 --- a/pkg/operator/operator_deployment.go +++ b/pkg/operator/operator_deployment.go @@ -23,7 +23,6 @@ package operator import ( - "context" "fmt" "github.com/pkg/errors" @@ -46,7 +45,7 @@ var ( // run the deployments part of the operator. // This registers a listener and waits until the process stops. -func (o *Operator) runDeployments() { +func (o *Operator) runDeployments(stop <-chan struct{}) { source := cache.NewListWatchFromClient( o.Dependencies.CRCli.DatabaseV1alpha().RESTClient(), api.ArangoDeploymentResourcePlural, @@ -59,9 +58,7 @@ func (o *Operator) runDeployments() { DeleteFunc: o.onDeleteArangoDeployment, }, cache.Indexers{}) - ctx := context.TODO() - // TODO: use workqueue to avoid blocking - informer.Run(ctx.Done()) + informer.Run(stop) } // onAddArangoDeployment deployment addition callback diff --git a/pkg/operator/operator_leader.go b/pkg/operator/operator_leader.go new file mode 100644 index 000000000..5d94c2058 --- /dev/null +++ b/pkg/operator/operator_leader.go @@ -0,0 +1,60 @@ +// +// 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 operator + +import ( + "time" + + "k8s.io/client-go/tools/leaderelection" + "k8s.io/client-go/tools/leaderelection/resourcelock" +) + +func (o *Operator) runLeaderElection(lockName string, onStart func(stop <-chan struct{})) { + namespace := o.Config.Namespace + kubecli := o.Dependencies.KubeCli + log := o.log.With().Str("lock-name", lockName).Logger() + rl, err := resourcelock.New(resourcelock.EndpointsResourceLock, + namespace, + lockName, + kubecli.CoreV1(), + resourcelock.ResourceLockConfig{ + Identity: o.Config.ID, + EventRecorder: o.Dependencies.EventRecorder, + }) + if err != nil { + log.Fatal().Err(err).Msg("Failed to create resource lock") + } + + leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{ + Lock: rl, + LeaseDuration: 15 * time.Second, + RenewDeadline: 10 * time.Second, + RetryPeriod: 2 * time.Second, + Callbacks: leaderelection.LeaderCallbacks{ + OnStartedLeading: onStart, + OnStoppedLeading: func() { + log.Info().Msg("Leader election lost") + }, + }, + }) +} diff --git a/pkg/operator/operator_local_storage.go b/pkg/operator/operator_local_storage.go index 9148d96e7..549a17fd8 100644 --- a/pkg/operator/operator_local_storage.go +++ b/pkg/operator/operator_local_storage.go @@ -23,7 +23,6 @@ package operator import ( - "context" "fmt" "github.com/pkg/errors" @@ -46,7 +45,7 @@ var ( // run the local storages part of the operator. // This registers a listener and waits until the process stops. -func (o *Operator) runLocalStorages() { +func (o *Operator) runLocalStorages(stop <-chan struct{}) { source := cache.NewListWatchFromClient( o.Dependencies.CRCli.StorageV1alpha().RESTClient(), api.ArangoLocalStorageResourcePlural, @@ -59,9 +58,7 @@ func (o *Operator) runLocalStorages() { DeleteFunc: o.onDeleteArangoLocalStorage, }, cache.Indexers{}) - ctx := context.TODO() - // TODO: use workqueue to avoid blocking - informer.Run(ctx.Done()) + informer.Run(stop) } // onAddArangoLocalStorage local storage addition callback diff --git a/pkg/util/arangod/client.go b/pkg/util/arangod/client.go index 95459a579..8b36da1e1 100644 --- a/pkg/util/arangod/client.go +++ b/pkg/util/arangod/client.go @@ -24,6 +24,7 @@ package arangod import ( "context" + "crypto/tls" "fmt" "net" nhttp "net/http" @@ -70,6 +71,19 @@ var ( TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } + sharedHTTPSTransport = &nhttp.Transport{ + Proxy: nhttp.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } ) // CreateArangodClient creates a go-driver client for a specific member in the given group. @@ -97,11 +111,16 @@ func CreateArangodDatabaseClient(ctx context.Context, cli corev1.CoreV1Interface // CreateArangodClientForDNSName creates a go-driver client for a given DNS name. func createArangodClientForDNSName(ctx context.Context, cli corev1.CoreV1Interface, apiObject *api.ArangoDeployment, dnsName string) (driver.Client, error) { scheme := "http" + transport := sharedHTTPTransport + if apiObject.Spec.IsSecure() { + scheme = "https" + transport = sharedHTTPSTransport + } connConfig := http.ConnectionConfig{ Endpoints: []string{scheme + "://" + net.JoinHostPort(dnsName, strconv.Itoa(k8sutil.ArangoPort))}, - Transport: sharedHTTPTransport, + Transport: transport, } - // TODO deal with TLS + // TODO deal with TLS with proper CA checking conn, err := http.NewConnection(connConfig) if err != nil { return nil, maskAny(err) diff --git a/pkg/util/crd/crd.go b/pkg/util/crd/crd.go index 4113d0b35..966a1b847 100644 --- a/pkg/util/crd/crd.go +++ b/pkg/util/crd/crd.go @@ -26,42 +26,13 @@ import ( "fmt" "time" - "k8s.io/apimachinery/pkg/runtime/schema" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/retry" ) -// CreateCRD creates a custom resouce definition. -func CreateCRD(clientset apiextensionsclient.Interface, groupVersion schema.GroupVersion, crdName, rkind, rplural string, shortName ...string) error { - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: crdName, - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: groupVersion.Group, - Version: groupVersion.Version, - Scope: apiextensionsv1beta1.NamespaceScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: rplural, - Kind: rkind, - }, - }, - } - if len(shortName) != 0 { - crd.Spec.Names.ShortNames = shortName - } - _, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) - if err != nil && !k8sutil.IsAlreadyExists(err) { - return err - } - return nil -} - // WaitCRDReady waits for a custom resource definition with given name to be ready. func WaitCRDReady(clientset apiextensionsclient.Interface, crdName string) error { op := func() error { diff --git a/scripts/kube_run_tests.sh b/scripts/kube_run_tests.sh new file mode 100755 index 000000000..e8f657033 --- /dev/null +++ b/scripts/kube_run_tests.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Run kubectl run to run the integration tests. + +DEPLOYMENTNAMESPACE=$1 +TESTIMAGE=$2 +ENTERPRISEIMAGE=$3 +TESTTIMEOUT=$4 +TESTLENGTHOPTIONS=$5 + +IMAGEID=$(docker inspect ${TESTIMAGE} '--format={{index .RepoDigests 0}}') + +kubectl --namespace ${DEPLOYMENTNAMESPACE} \ + run arangodb-operator-test -i --rm --quiet --restart=Never \ + --image=${IMAGEID} \ + --env="ENTERPRISEIMAGE=${ENTERPRISEIMAGE}" \ + --env="TEST_NAMESPACE=${DEPLOYMENTNAMESPACE}" \ + -- \ + -test.v -test.timeout $TESTTIMEOUT $TESTLENGTHOPTIONS $TESTOPTIONS diff --git a/tools/manifests/manifest_builder.go b/tools/manifests/manifest_builder.go index 6eca6afe6..0919e9482 100644 --- a/tools/manifests/manifest_builder.go +++ b/tools/manifests/manifest_builder.go @@ -38,40 +38,58 @@ import ( var ( options struct { - OutputFile string + OutputSuffix string TemplatesDir string - Namespace string - Image string - ImagePullPolicy string - ImageSHA256 bool - OperatorName string - RBAC bool + Namespace string + Image string + ImagePullPolicy string + ImageSHA256 bool + DeploymentOperatorName string + StorageOperatorName string + RBAC bool } - templateNames = []string{ + deploymentTemplateNames = []string{ + "rbac.yaml", + "deployment.yaml", + } + storageTemplateNames = []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.OutputSuffix, "output-suffix", "", "Suffix of the generated manifest files") 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.StringVar(&options.DeploymentOperatorName, "deployment-operator-name", "arango-deployment-operator", "Name of the ArangoDeployment operator deployment") + pflag.StringVar(&options.StorageOperatorName, "storage-operator-name", "arango-storage-operator", "Name of the ArangoLocalStorage operator deployment") pflag.BoolVar(&options.RBAC, "rbac", true, "Use role based access control") pflag.Parse() } +type TemplateOptions struct { + Image string + ImagePullPolicy string + RBAC bool + Deployment OperatorOptions + Storage OperatorOptions +} + +type OperatorOptions struct { + Namespace string + OperatorName string + ClusterRoleName string + ClusterRoleBindingName string +} + func main() { // Check options - if options.OutputFile == "" { - log.Fatal("--output not specified.") - } if options.Namespace == "" { log.Fatal("--namespace not specified.") } @@ -95,47 +113,55 @@ func main() { 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") + // Prepare templates to include + templateNameSet := map[string][]string{ + "deployment": deploymentTemplateNames, + "storage": storageTemplateNames, } - // Save output - outputPath, err := filepath.Abs(options.OutputFile) - if err != nil { - log.Fatalf("Failed to get absolute output path: %v\n", err) + // Process templates + templateOptions := TemplateOptions{ + Image: options.Image, + ImagePullPolicy: options.ImagePullPolicy, + RBAC: options.RBAC, + Deployment: OperatorOptions{ + Namespace: options.Namespace, + OperatorName: options.DeploymentOperatorName, + ClusterRoleName: "arango-deployment-operator", + ClusterRoleBindingName: "arango-deployment-operator", + }, + Storage: OperatorOptions{ + Namespace: options.Namespace, + OperatorName: options.StorageOperatorName, + ClusterRoleName: "arango-storage-operator", + ClusterRoleBindingName: "arango-storage-operator", + }, } - 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) + for group, templateNames := range templateNameSet { + output := &bytes.Buffer{} + for i, name := range templateNames { + t, err := template.New(name).ParseFiles(filepath.Join(options.TemplatesDir, group, 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/%s\n", group, name)) + t.Execute(output, templateOptions) + output.WriteString("\n") + } + + // Save output + outputPath, err := filepath.Abs(filepath.Join("manifests", "arango-"+group+options.OutputSuffix+".yaml")) + 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) + } } }