diff --git a/Jenkinsfile.groovy b/Jenkinsfile.groovy index 8d7f2f1de..3e383bfd7 100644 --- a/Jenkinsfile.groovy +++ b/Jenkinsfile.groovy @@ -25,6 +25,7 @@ pipeline { } agent any 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: '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)', ) @@ -48,11 +49,12 @@ pipeline { lock("${params.TESTNAMESPACE}-${env.GIT_COMMIT}") { withCredentials([string(credentialsId: 'ENTERPRISEIMAGE', variable: 'DEFAULTENTERPRISEIMAGE')]) { withEnv([ - "KUBECONFIG=${params.KUBECONFIG}", - "TESTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", - "IMAGETAG=${env.GIT_COMMIT}", - "PUSHIMAGES=1", "ENTERPRISEIMAGE=${params.ENTERPRISEIMAGE}", + "IMAGETAG=${env.GIT_COMMIT}", + "KUBECONFIG=${params.KUBECONFIG}", + "LONG=${params.LONG ? 1 : 0}", + "PUSHIMAGES=1", + "TESTNAMESPACE=${params.TESTNAMESPACE}-${env.GIT_COMMIT}", ]) { sh "make run-tests" } diff --git a/Makefile b/Makefile index f2617f04b..2f57c880d 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,12 @@ GHRELEASE := $(GOBUILDDIR)/bin/github-release ifndef TESTNAMESPACE TESTNAMESPACE := arangodb-operator-tests endif +TESTLENGTHOPTIONS := -test.short +TESTTIMEOUT := 20m +ifeq ($(LONG), 1) + TESTLENGTHOPTIONS := + TESTTIMEOUT := 40m +endif SOURCES := $(shell find $(SRCDIR) -name '*.go' -not -path './test/*') @@ -161,7 +167,8 @@ run-unit-tests: $(GOBUILDDIR) $(SOURCES) golang:$(GOVERSION) \ go test -v \ $(REPOPATH)/pkg/apis/arangodb/v1alpha \ - $(REPOPATH)/pkg/deployment + $(REPOPATH)/pkg/deployment \ + $(REPOPATH)/pkg/util/k8sutil $(TESTBIN): $(GOBUILDDIR) $(SOURCES) @mkdir -p $(BINDIR) @@ -194,7 +201,7 @@ endif --env="ENTERPRISEIMAGE=$(ENTERPRISEIMAGE)" \ --env="TEST_NAMESPACE=$(TESTNAMESPACE)" \ -- \ - -test.v + -test.v -test.timeout $(TESTTIMEOUT) $(TESTLENGTHOPTIONS) kubectl delete namespace $(TESTNAMESPACE) --ignore-not-found --now cleanup-tests: diff --git a/docs/user/custom_resource.md b/docs/user/custom_resource.md index c7c6c89e9..9a0c17d1c 100644 --- a/docs/user/custom_resource.md +++ b/docs/user/custom_resource.md @@ -101,8 +101,8 @@ This setting specifies the type of storage engine used for all servers in the cluster. Possible values are: -- `mmfiles` (default) To use the MMfiles storage engine. -- `rocksdb` To use the RocksDB storage engine. +- `mmfiles` To use the MMfiles storage engine. +- `rocksdb` (default) To use the RocksDB storage engine. This setting cannot be changed after the cluster has been created. diff --git a/pkg/apis/arangodb/v1alpha/deployment_spec.go b/pkg/apis/arangodb/v1alpha/deployment_spec.go index 71c24c7da..df33b2e03 100644 --- a/pkg/apis/arangodb/v1alpha/deployment_spec.go +++ b/pkg/apis/arangodb/v1alpha/deployment_spec.go @@ -82,7 +82,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) { s.Environment = EnvironmentDevelopment } if s.StorageEngine == "" { - s.StorageEngine = StorageEngineMMFiles + s.StorageEngine = StorageEngineRocksDB } if s.Image == "" && s.IsDevelopment() { s.Image = defaultImage diff --git a/pkg/util/k8sutil/affinity_test.go b/pkg/util/k8sutil/affinity_test.go new file mode 100644 index 000000000..2e1f3de5d --- /dev/null +++ b/pkg/util/k8sutil/affinity_test.go @@ -0,0 +1,97 @@ +// +// 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 k8sutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCreateAffinity tests createAffinity +func TestCreateAffinity(t *testing.T) { + // Required + a := createAffinity("test", "role", true, "") + assert.Nil(t, a.PodAffinity) + require.NotNil(t, a.PodAntiAffinity) + require.Len(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 1) + assert.Len(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 0) + assert.Equal(t, "kubernetes.io/hostname", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].TopologyKey) + require.NotNil(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector) + assert.Equal(t, "test", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["app"]) + assert.Equal(t, "role", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["role"]) + + // Require & affinity with role dbserver + a = createAffinity("test", "role", true, "dbserver") + require.NotNil(t, a.PodAffinity) + require.Len(t, a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 1) + assert.Len(t, a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 0) + assert.Equal(t, "kubernetes.io/hostname", a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].TopologyKey) + require.NotNil(t, a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector) + assert.Equal(t, "test", a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["app"]) + assert.Equal(t, "dbserver", a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["role"]) + + require.NotNil(t, a.PodAntiAffinity) + require.Len(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 1) + assert.Len(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 0) + assert.Equal(t, "kubernetes.io/hostname", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].TopologyKey) + require.NotNil(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector) + assert.Equal(t, "test", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["app"]) + assert.Equal(t, "role", a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["role"]) + + // Not Required + a = createAffinity("test", "role", false, "") + assert.Nil(t, a.PodAffinity) + require.NotNil(t, a.PodAntiAffinity) + assert.Len(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 0) + require.Len(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 1) + assert.Equal(t, "kubernetes.io/hostname", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.TopologyKey) + require.NotNil(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector) + assert.Equal(t, "test", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["app"]) + assert.Equal(t, "role", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["role"]) + + // Not Required & affinity with role dbserver + a = createAffinity("test", "role", false, "dbserver") + require.NotNil(t, a.PodAffinity) + require.Len(t, a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 1) + assert.Len(t, a.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 0) + assert.Equal(t, "kubernetes.io/hostname", a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.TopologyKey) + require.NotNil(t, a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector) + assert.Equal(t, "test", a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["app"]) + assert.Equal(t, "dbserver", a.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["role"]) + + require.NotNil(t, a.PodAntiAffinity) + require.Len(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 1) + assert.Len(t, a.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 0) + assert.Equal(t, "kubernetes.io/hostname", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.TopologyKey) + require.NotNil(t, a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector) + assert.Equal(t, "test", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["arango_deployment"]) + assert.Equal(t, "arangodb", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["app"]) + assert.Equal(t, "role", a.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.LabelSelector.MatchLabels["role"]) +} diff --git a/tests/auth_test.go b/tests/auth_test.go index 79421c3d8..b1fb11b55 100644 --- a/tests/auth_test.go +++ b/tests/auth_test.go @@ -17,6 +17,7 @@ import ( // TestAuthenticationSingleDefaultSecret creating a single server // with default authentication (on) using a generated JWT secret. func TestAuthenticationSingleDefaultSecret(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) @@ -63,6 +64,7 @@ func TestAuthenticationSingleDefaultSecret(t *testing.T) { // TestAuthenticationSingleCustomSecret creating a single server // with default authentication (on) using a user created JWT secret. func TestAuthenticationSingleCustomSecret(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) @@ -113,6 +115,7 @@ func TestAuthenticationSingleCustomSecret(t *testing.T) { // TestAuthenticationNoneSingle creating a single server // with authentication set to `None`. func TestAuthenticationNoneSingle(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) @@ -150,6 +153,7 @@ func TestAuthenticationNoneSingle(t *testing.T) { // TestAuthenticationClusterDefaultSecret creating a cluster // with default authentication (on) using a generated JWT secret. func TestAuthenticationClusterDefaultSecret(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) @@ -196,6 +200,7 @@ func TestAuthenticationClusterDefaultSecret(t *testing.T) { // TestAuthenticationClusterCustomSecret creating a cluster // with default authentication (on) using a user created JWT secret. func TestAuthenticationClusterCustomSecret(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) @@ -246,7 +251,7 @@ func TestAuthenticationClusterCustomSecret(t *testing.T) { // TestAuthenticationNoneCluster creating a cluster // with authentication set to `None`. func TestAuthenticationNoneCluster(t *testing.T) { - // Do not allow to run this test in parallel, because it is already claiming a lot of resources. + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) diff --git a/tests/rocksdb_encryption_test.go b/tests/rocksdb_encryption_test.go index a357f2000..ab85e00a3 100644 --- a/tests/rocksdb_encryption_test.go +++ b/tests/rocksdb_encryption_test.go @@ -16,6 +16,7 @@ import ( // TestRocksDBEncryptionSingle tests the creating of a single server deployment // with RocksDB & Encryption. func TestRocksDBEncryptionSingle(t *testing.T) { + longOrSkip(t) image := getEnterpriseImageOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) diff --git a/tests/scale_test.go b/tests/scale_test.go index 62189ad4b..81ea39aea 100644 --- a/tests/scale_test.go +++ b/tests/scale_test.go @@ -14,6 +14,7 @@ import ( // TestScaleCluster tests scaling up/down the number of DBServers & coordinators // of a cluster. func TestScaleCluster(t *testing.T) { + longOrSkip(t) c := client.MustNewInCluster() kubecli := mustNewKubeClient(t) ns := getNamespace(t) diff --git a/tests/simple_test.go b/tests/simple_test.go new file mode 100644 index 000000000..7feff8a7b --- /dev/null +++ b/tests/simple_test.go @@ -0,0 +1,136 @@ +package tests + +import ( + "context" + "testing" + + "github.com/dchest/uniuri" + "github.com/stretchr/testify/assert" + + driver "github.com/arangodb/go-driver" + api "github.com/arangodb/k8s-operator/pkg/apis/arangodb/v1alpha" + "github.com/arangodb/k8s-operator/pkg/client" +) + +// TestSimpleSingle tests the creating of a single server deployment +// with default settings. +func TestSimpleSingle(t *testing.T) { + c := client.MustNewInCluster() + kubecli := mustNewKubeClient(t) + ns := getNamespace(t) + + // Prepare deployment config + depl := newDeployment("test-sng-" + uniuri.NewLen(4)) + depl.Spec.Mode = api.DeploymentModeSingle + + // Create deployment + _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) + if err != nil { + t.Fatalf("Create deployment failed: %v", err) + } + // Prepare cleanup + defer removeDeployment(c, depl.GetName(), ns) + + // Wait for deployment to be ready + apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentHasState(api.DeploymentStateRunning)) + if err != nil { + t.Errorf("Deployment not running in time: %v", err) + } + + // Create a database client + ctx := context.Background() + client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t) + + // Wait for single server available + if err := waitUntilVersionUp(client); err != nil { + t.Fatalf("Single server not running returning version in time: %v", err) + } + + // Check server role + assert.NoError(t, client.SynchronizeEndpoints(ctx)) + role, err := client.ServerRole(ctx) + assert.NoError(t, err) + assert.Equal(t, driver.ServerRoleSingle, role) +} + +// TestSimpleResilientSingle tests the creating of a resilientsingle server deployment +// with default settings. +func TestSimpleResilientSingle(t *testing.T) { + c := client.MustNewInCluster() + kubecli := mustNewKubeClient(t) + ns := getNamespace(t) + + // Prepare deployment config + depl := newDeployment("test-rs-" + uniuri.NewLen(4)) + depl.Spec.Mode = api.DeploymentModeResilientSingle + + // Create deployment + _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) + if err != nil { + t.Fatalf("Create deployment failed: %v", err) + } + // Prepare cleanup + defer removeDeployment(c, depl.GetName(), ns) + + // Wait for deployment to be ready + apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentHasState(api.DeploymentStateRunning)) + if err != nil { + t.Errorf("Deployment not running in time: %v", err) + } + + // Create a database client + ctx := context.Background() + client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t) + + // Wait for single server available + if err := waitUntilVersionUp(client); err != nil { + t.Fatalf("ResilientSingle servers not running returning version in time: %v", err) + } + + // Check server role + assert.NoError(t, client.SynchronizeEndpoints(ctx)) + role, err := client.ServerRole(ctx) + assert.NoError(t, err) + assert.Equal(t, driver.ServerRoleSingleActive, role) +} + +// TestSimpleCluster tests the creating of a cluster deployment +// with default settings. +func TestSimpleCluster(t *testing.T) { + c := client.MustNewInCluster() + kubecli := mustNewKubeClient(t) + ns := getNamespace(t) + + // Prepare deployment config + depl := newDeployment("test-cls-" + uniuri.NewLen(4)) + depl.Spec.Mode = api.DeploymentModeCluster + + // Create deployment + _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) + if err != nil { + t.Fatalf("Create deployment failed: %v", err) + } + // Prepare cleanup + defer removeDeployment(c, depl.GetName(), ns) + + // Wait for deployment to be ready + apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentHasState(api.DeploymentStateRunning)) + if err != nil { + t.Errorf("Deployment not running in time: %v", err) + } + + // Create a database client + ctx := context.Background() + client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t) + + // Wait for single server available + if err := waitUntilVersionUp(client); err != nil { + t.Fatalf("Cluster not running returning version in time: %v", err) + } + + // Check server role + assert.NoError(t, client.SynchronizeEndpoints(ctx)) + role, err := client.ServerRole(ctx) + assert.NoError(t, err) + assert.Equal(t, driver.ServerRoleCoordinator, role) +} diff --git a/tests/single_test.go b/tests/single_test.go deleted file mode 100644 index b9fa7363d..000000000 --- a/tests/single_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/dchest/uniuri" - - api "github.com/arangodb/k8s-operator/pkg/apis/arangodb/v1alpha" - "github.com/arangodb/k8s-operator/pkg/client" -) - -// TestSimpleSingle tests the creating of a single server deployment -// with default settings. -func TestSimpleSingle(t *testing.T) { - c := client.MustNewInCluster() - ns := getNamespace(t) - - // Prepare deployment config - depl := newDeployment("test-single-" + uniuri.NewLen(4)) - depl.Spec.Mode = api.DeploymentModeSingle - - // Create deployment - _, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl) - if err != nil { - t.Fatalf("Create deployment failed: %v", err) - } - - // Wait for deployment to be ready - if _, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentHasState(api.DeploymentStateRunning)); err != nil { - t.Errorf("Deployment not running in time: %v", err) - } - - // Cleanup - removeDeployment(c, depl.GetName(), ns) -} diff --git a/tests/test_util.go b/tests/test_util.go index 0678bd76a..3abe20224 100644 --- a/tests/test_util.go +++ b/tests/test_util.go @@ -52,6 +52,15 @@ var ( maskAny = errors.WithStack ) +// longOrSkip checks the short test flag. +// If short is set, the current test is skipped. +// If not, this function returns as normal. +func longOrSkip(t *testing.T) { + if testing.Short() { + t.Skip("Test skipped in short test") + } +} + // getEnterpriseImageOrSkip returns the docker image used for enterprise // tests. If empty, enterprise tests are skipped. func getEnterpriseImageOrSkip(t *testing.T) string {