From 6e9cb0f3e3eb18b67ea18d65397b22ed27ac555c Mon Sep 17 00:00:00 2001 From: Ewout Prangsma Date: Fri, 8 Jun 2018 08:10:05 +0200 Subject: [PATCH 1/2] Added unit tests for pv_creator.go --- Makefile | 1 + pkg/storage/provisioner/mocks/mocks.go | 36 ++++ pkg/storage/provisioner/mocks/provisioner.go | 95 +++++++++ pkg/storage/pv_creator_test.go | 209 +++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 pkg/storage/provisioner/mocks/mocks.go create mode 100644 pkg/storage/provisioner/mocks/provisioner.go create mode 100644 pkg/storage/pv_creator_test.go diff --git a/Makefile b/Makefile index dfd8ea227..bc39e4611 100644 --- a/Makefile +++ b/Makefile @@ -235,6 +235,7 @@ run-unit-tests: $(GOBUILDDIR) $(SOURCES) $(REPOPATH)/pkg/apis/storage/v1alpha \ $(REPOPATH)/pkg/deployment/reconcile \ $(REPOPATH)/pkg/deployment/resources \ + $(REPOPATH)/pkg/storage \ $(REPOPATH)/pkg/util/k8sutil \ $(REPOPATH)/pkg/util/k8sutil/test \ $(REPOPATH)/pkg/util/probe \ diff --git a/pkg/storage/provisioner/mocks/mocks.go b/pkg/storage/provisioner/mocks/mocks.go new file mode 100644 index 000000000..a3feb1728 --- /dev/null +++ b/pkg/storage/provisioner/mocks/mocks.go @@ -0,0 +1,36 @@ +// +// 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 mocks + +import ( + "github.com/stretchr/testify/mock" +) + +type MockGetter interface { + AsMock() *mock.Mock +} + +// AsMock performs a typeconversion to *Mock. +func AsMock(obj interface{}) *mock.Mock { + return obj.(MockGetter).AsMock() +} diff --git a/pkg/storage/provisioner/mocks/provisioner.go b/pkg/storage/provisioner/mocks/provisioner.go new file mode 100644 index 000000000..be3918036 --- /dev/null +++ b/pkg/storage/provisioner/mocks/provisioner.go @@ -0,0 +1,95 @@ +// +// 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 mocks + +import ( + "context" + "fmt" + + "github.com/stretchr/testify/mock" + + "github.com/arangodb/kube-arangodb/pkg/storage/provisioner" +) + +type Provisioner interface { + provisioner.API + MockGetter +} + +type provisionerMock struct { + mock.Mock + nodeName string + available, capacity int64 + localPaths map[string]struct{} +} + +// NewProvisioner returns a new mocked provisioner +func NewProvisioner(nodeName string, available, capacity int64) Provisioner { + return &provisionerMock{ + nodeName: nodeName, + available: available, + capacity: capacity, + localPaths: make(map[string]struct{}), + } +} + +func (m *provisionerMock) AsMock() *mock.Mock { + return &m.Mock +} + +// GetNodeInfo fetches information from the current node. +func (m *provisionerMock) GetNodeInfo(ctx context.Context) (provisioner.NodeInfo, error) { + return provisioner.NodeInfo{ + NodeName: m.nodeName, + }, nil +} + +// GetInfo fetches information from the filesystem containing +// the given local path on the current node. +func (m *provisionerMock) GetInfo(ctx context.Context, localPath string) (provisioner.Info, error) { + return provisioner.Info{ + NodeInfo: provisioner.NodeInfo{ + NodeName: m.nodeName, + }, + Available: m.available, + Capacity: m.capacity, + }, nil +} + +// Prepare a volume at the given local path +func (m *provisionerMock) Prepare(ctx context.Context, localPath string) error { + if _, found := m.localPaths[localPath]; found { + return fmt.Errorf("Path already exists: %s", localPath) + } + m.localPaths[localPath] = struct{}{} + return nil +} + +// Remove a volume with the given local path +func (m *provisionerMock) Remove(ctx context.Context, localPath string) error { + if _, found := m.localPaths[localPath]; !found { + return fmt.Errorf("Path not found: %s", localPath) + } + delete(m.localPaths, localPath) + return nil +} diff --git a/pkg/storage/pv_creator_test.go b/pkg/storage/pv_creator_test.go new file mode 100644 index 000000000..599ad3a30 --- /dev/null +++ b/pkg/storage/pv_creator_test.go @@ -0,0 +1,209 @@ +// +// 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 storage + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/arangodb/kube-arangodb/pkg/storage/provisioner" + "github.com/arangodb/kube-arangodb/pkg/storage/provisioner/mocks" +) + +// TestCreateValidEndpointList tests createValidEndpointList. +func TestCreateValidEndpointList(t *testing.T) { + tests := []struct { + Input *v1.EndpointsList + Expected []string + }{ + { + Input: &v1.EndpointsList{}, + Expected: []string{}, + }, + { + Input: &v1.EndpointsList{ + Items: []v1.Endpoints{ + v1.Endpoints{ + Subsets: []v1.EndpointSubset{ + v1.EndpointSubset{ + Addresses: []v1.EndpointAddress{ + v1.EndpointAddress{ + IP: "1.2.3.4", + }, + }, + }, + v1.EndpointSubset{ + Addresses: []v1.EndpointAddress{ + v1.EndpointAddress{ + IP: "5.6.7.8", + }, + v1.EndpointAddress{ + IP: "9.10.11.12", + }, + }, + }, + }, + }, + }, + }, + Expected: []string{ + "1.2.3.4:8929", + "5.6.7.8:8929", + "9.10.11.12:8929", + }, + }, + } + for _, test := range tests { + output := createValidEndpointList(test.Input) + assert.Equal(t, test.Expected, output) + } +} + +// TestCreateNodeAffinity tests createNodeAffinity. +func TestCreateNodeAffinity(t *testing.T) { + tests := map[string]string{ + "foo": "{\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"kubernetes.io/hostname\",\"operator\":\"In\",\"values\":[\"foo\"]}]}]}}", + "bar": "{\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"kubernetes.io/hostname\",\"operator\":\"In\",\"values\":[\"bar\"]}]}]}}", + } + for input, expected := range tests { + output, err := createNodeAffinity(input) + assert.NoError(t, err) + assert.Equal(t, expected, output, "Input: '%s'", input) + } +} + +// TestCreateNodeClientMap tests createNodeClientMap. +func TestCreateNodeClientMap(t *testing.T) { + GB := int64(1024 * 1024 * 1024) + foo := mocks.NewProvisioner("foo", 100*GB, 100*GB) + bar := mocks.NewProvisioner("bar", 100*GB, 100*GB) + tests := []struct { + Input []provisioner.API + Expected map[string]provisioner.API + }{ + { + Input: nil, + Expected: map[string]provisioner.API{}, + }, + { + Input: []provisioner.API{foo, bar}, + Expected: map[string]provisioner.API{ + "bar": bar, + "foo": foo, + }, + }, + } + ctx := context.Background() + for _, test := range tests { + output := createNodeClientMap(ctx, test.Input) + assert.Equal(t, test.Expected, output) + } +} + +// TestGetDeploymentInfo tests getDeploymentInfo. +func TestGetDeploymentInfo(t *testing.T) { + tests := []struct { + Input v1.PersistentVolumeClaim + ExpectedDeploymentName string + ExpectedRole string + ExpectedEnforceAntiAffinity bool + }{ + { + Input: v1.PersistentVolumeClaim{}, + ExpectedDeploymentName: "", + ExpectedRole: "", + ExpectedEnforceAntiAffinity: false, + }, + { + Input: v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "database.arangodb.com/enforce-anti-affinity": "true", + }, + Labels: map[string]string{ + "arango_deployment": "foo", + "role": "r1", + }, + }, + }, + ExpectedDeploymentName: "foo", + ExpectedRole: "r1", + ExpectedEnforceAntiAffinity: true, + }, + { + Input: v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "database.arangodb.com/enforce-anti-affinity": "false", + }, + Labels: map[string]string{ + "arango_deployment": "foo", + "role": "r1", + }, + }, + }, + ExpectedDeploymentName: "foo", + ExpectedRole: "r1", + ExpectedEnforceAntiAffinity: false, + }, + { + Input: v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "database.arangodb.com/enforce-anti-affinity": "wrong", + }, + Labels: map[string]string{ + "arango_deployment": "bar", + "role": "r77", + }, + }, + }, + ExpectedDeploymentName: "bar", + ExpectedRole: "r77", + ExpectedEnforceAntiAffinity: false, + }, + } + for _, test := range tests { + deploymentName, role, enforceAntiAffinity := getDeploymentInfo(test.Input) + assert.Equal(t, test.ExpectedDeploymentName, deploymentName) + assert.Equal(t, test.ExpectedRole, role) + assert.Equal(t, test.ExpectedEnforceAntiAffinity, enforceAntiAffinity) + } +} + +// TestShortHash tests shortHash. +func TestShortHash(t *testing.T) { + tests := map[string]string{ + "foo": "0beec7", + "": "da39a3", + "something very very very very very looooooooooooooooooooooooooooooooong": "68ff76", + } + for input, expected := range tests { + output := shortHash(input) + assert.Equal(t, expected, output, "Input: '%s'", input) + } +} From 3e325e842a47866faaa480aaa6bb82f0da475ac8 Mon Sep 17 00:00:00 2001 From: Ewout Prangsma Date: Fri, 15 Jun 2018 07:31:09 +0200 Subject: [PATCH 2/2] Using alternate values --- pkg/storage/pv_creator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/storage/pv_creator_test.go b/pkg/storage/pv_creator_test.go index 599ad3a30..bbc4311a2 100644 --- a/pkg/storage/pv_creator_test.go +++ b/pkg/storage/pv_creator_test.go @@ -99,8 +99,8 @@ func TestCreateNodeAffinity(t *testing.T) { // TestCreateNodeClientMap tests createNodeClientMap. func TestCreateNodeClientMap(t *testing.T) { GB := int64(1024 * 1024 * 1024) - foo := mocks.NewProvisioner("foo", 100*GB, 100*GB) - bar := mocks.NewProvisioner("bar", 100*GB, 100*GB) + foo := mocks.NewProvisioner("foo", 100*GB, 200*GB) + bar := mocks.NewProvisioner("bar", 300*GB, 400*GB) tests := []struct { Input []provisioner.API Expected map[string]provisioner.API