Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tests for controller with mock #344

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e8ecfb8
add tests for controller with mock
Oct 11, 2019
4d281cc
merge from master to branch
Oct 11, 2019
3e6fe19
add package
Oct 11, 2019
485deb5
update api version in tests
Oct 11, 2019
6f593de
remove status update instance directly
Oct 11, 2019
7a78216
fix controllerutil crash in tests and move mock
Oct 14, 2019
c02963f
update to run mock and azure based on flag
Oct 14, 2019
71fc0ee
Merge branch 'master' into feat/99-SQLControllerTestsWithMock
priyakumarank Oct 14, 2019
6b1b30f
Merge branch 'master' into feat/99-SQLControllerTestsWithMock
priyakumarank Oct 14, 2019
81597ec
enable tests to run in parallel
Oct 14, 2019
e2e213b
increase timeout
Oct 14, 2019
55bf533
run sql controller with mock always
Oct 14, 2019
fdf622b
merge from master to branch
Oct 14, 2019
5756c4d
rename test files
Oct 14, 2019
3372db8
run sql with mock always
Oct 15, 2019
90aef7d
update test
Oct 15, 2019
5eb7841
Merge branch 'master' into feat/99-SQLControllerTestsWithMock
priyakumarank Oct 15, 2019
f3ddfdf
Merge branch 'master' into feat/99-SQLControllerTestsWithMock
priyakumarank Oct 15, 2019
e49f9eb
enable parallel tests
Oct 15, 2019
32af2b3
Merge branch 'feat/99-SQLControllerTestsWithMock' of https://github.c…
Oct 15, 2019
2f31e9f
run sql using mock
Oct 15, 2019
4a429eb
disable tests running in parallel
Oct 15, 2019
706887d
merge from master to branch
Oct 20, 2019
64e1aca
remove tests and add status subresource back
Oct 20, 2019
fc325eb
merge from master and fix conflicts
Oct 21, 2019
06b4059
add interface methods
Oct 21, 2019
d3dba00
update status
Oct 21, 2019
8a4f788
refactor gosdkclient
Oct 23, 2019
5dd33bd
refactor sqlclient gosdk
Oct 23, 2019
f777198
use resourceclient
Oct 23, 2019
f59f811
remove redundant file
Oct 24, 2019
77c8ae5
enable parallel tests
Oct 24, 2019
3088535
add tests and fix issues
Oct 25, 2019
1ca646c
merge from master to branch
Oct 25, 2019
c441016
disable parallel tests
Oct 25, 2019
11ac815
use requeue_after env variable
Oct 25, 2019
ca48085
Merge branch 'master' into feat/99-SQLControllerTestsWithMock
priyakumarank Oct 27, 2019
52a13d6
make requeue time configurablle in eh ns
Oct 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ api-test: generate fmt vet manifests

# Run tests
test: generate fmt vet manifests
TEST_USE_EXISTING_CLUSTER=false TEST_CONTROLLER_WITH_MOCKS=true go test -v -coverprofile=coverage.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... ./pkg/resourcemanager/storages/... 2>&1 | tee testlogs.txt
TEST_USE_EXISTING_CLUSTER=false TEST_CONTROLLER_WITH_MOCKS=true REQUEUE_AFTER=20 go test -v -coverprofile=coverage.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... ./pkg/resourcemanager/storages/... 2>&1 | tee testlogs.txt
go-junit-report < testlogs.txt > report.xml
go tool cover -html=coverage.txt -o cover.html

# Run tests with existing cluster
test-existing: generate fmt vet manifests
TEST_USE_EXISTING_CLUSTER=true TEST_CONTROLLER_WITH_MOCKS=false go test -v -coverprofile=coverage-existing.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... ./pkg/resourcemanager/storages/... 2>&1 | tee testlogs-existing.txt
TEST_USE_EXISTING_CLUSTER=true TEST_CONTROLLER_WITH_MOCKS=false REQUEUE_AFTER=20 go test -v -coverprofile=coverage-existing.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... ./pkg/resourcemanager/storages/... 2>&1 | tee testlogs-existing.txt
go-junit-report < testlogs-existing.txt > report-existing.xml
go tool cover -html=coverage-existing.txt -o cover-existing.html

Expand Down
9 changes: 9 additions & 0 deletions api/v1alpha1/azuresqldatabase_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
package v1alpha1

import (
helpers "github.com/Azure/azure-service-operator/pkg/helpers"
sql "github.com/Azure/azure-service-operator/pkg/resourcemanager/sqlclient"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -59,3 +60,11 @@ func init() {
func (s *AzureSqlDatabase) IsSubmitted() bool {
return s.Status.Provisioned
}

func (s *AzureSqlDatabase) HasFinalizer(finalizerName string) bool {
return helpers.ContainsString(s.ObjectMeta.Finalizers, finalizerName)
}

func (s *AzureSqlDatabase) IsBeingDeleted() bool {
return !s.ObjectMeta.DeletionTimestamp.IsZero()
}
19 changes: 6 additions & 13 deletions controllers/azuresqlaction_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ const AzureSqlActionFinalizerName = "azuresqlaction.finalizers.azure.com"
// AzureSqlActionReconciler reconciles a AzureSqlAction object
type AzureSqlActionReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
ResourceClient sql.ResourceClient
}

// +kubebuilder:rbac:groups=azure.microsoft.com,resources=azuresqlactions,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -120,12 +121,6 @@ func (r *AzureSqlActionReconciler) reconcileExternal(instance *azurev1alpha1.Azu
r.Recorder.Event(instance, corev1.EventTypeWarning, "Failed", "Unable to update instance")
}

sdkClient := sql.GoSDKClient{
Ctx: ctx,
ResourceGroupName: groupName,
ServerName: serverName,
}

//get owner instance of AzureSqlServer
r.Recorder.Event(instance, corev1.EventTypeNormal, "UpdatingOwner", "Updating owner AzureSqlServer instance")
var ownerInstance azurev1alpha1.AzureSqlServer
Expand All @@ -149,7 +144,7 @@ func (r *AzureSqlActionReconciler) reconcileExternal(instance *azurev1alpha1.Azu
}

// Get the Sql Server instance that corresponds to the Server name in the spec for this action
server, err := sdkClient.GetServer()
server, err := r.ResourceClient.GetServer(ctx, groupName, serverName)
if err != nil {
if strings.Contains(err.Error(), "ResourceGroupNotFound") {
r.Recorder.Event(instance, corev1.EventTypeWarning, "Failed", "Unable to get instance of AzureSqlServer: Resource group not found")
Expand All @@ -172,8 +167,6 @@ func (r *AzureSqlActionReconciler) reconcileExternal(instance *azurev1alpha1.Azu
}
}

sdkClient.Location = *server.Location

// rollcreds action
if strings.ToLower(instance.Spec.ActionName) == "rollcreds" {
azureSqlServerProperties := sql.SQLServerProperties{
Expand All @@ -185,7 +178,7 @@ func (r *AzureSqlActionReconciler) reconcileExternal(instance *azurev1alpha1.Azu
newPassword, _ := generateRandomPassword(passwordLength)
azureSqlServerProperties.AdministratorLoginPassword = to.StringPtr(newPassword)

if _, err := sdkClient.CreateOrUpdateSQLServer(azureSqlServerProperties); err != nil {
if _, err := r.ResourceClient.CreateOrUpdateSQLServer(ctx, groupName, *server.Location, serverName, azureSqlServerProperties); err != nil {
if !strings.Contains(err.Error(), "not complete") {
r.Recorder.Event(instance, corev1.EventTypeWarning, "Failed", "Unable to provision or update instance")
return errhelp.NewAzureError(err)
Expand Down
60 changes: 20 additions & 40 deletions controllers/azuresqldatabase_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package controllers
import (
"context"
"fmt"
"os"
"strconv"
"time"

"github.com/Azure/azure-service-operator/pkg/errhelp"
Expand All @@ -37,14 +39,13 @@ import (
azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1"
)

const azureSQLDatabaseFinalizerName = "azuresqldatabase.finalizers.azure.com"

// AzureSqlDatabaseReconciler reconciles a AzureSqlDatabase object
type AzureSqlDatabaseReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
ResourceClient sql.ResourceClient
}

// +kubebuilder:rbac:groups=azure.microsoft.com,resources=azuresqldatabases,verbs=get;list;watch;create;update;patch;delete
Expand All @@ -65,27 +66,32 @@ func (r *AzureSqlDatabaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
}

if helpers.IsBeingDeleted(&instance) {
if helpers.HasFinalizer(&instance, azureSQLDatabaseFinalizerName) {
if helpers.HasFinalizer(&instance, AzureSQLDatabaseFinalizerName) {
if err := r.deleteExternal(&instance); err != nil {
log.Info("Delete AzureSqlDatabase failed with ", "err", err.Error())
return ctrl.Result{}, err
}

helpers.RemoveFinalizer(&instance, azureSQLDatabaseFinalizerName)
if err := r.Update(context.Background(), &instance); err != nil {
helpers.RemoveFinalizer(&instance, AzureSQLDatabaseFinalizerName)
if err := r.Status().Update(context.Background(), &instance); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}

if !helpers.HasFinalizer(&instance, azureSQLDatabaseFinalizerName) {
if !instance.HasFinalizer(AzureSQLDatabaseFinalizerName) {
if err := r.addFinalizer(&instance); err != nil {
log.Info("Adding AzureSqlDatabase finalizer failed with ", "error", err.Error())
return ctrl.Result{}, err
}
}

requeueAfter, err := strconv.Atoi(os.Getenv("REQUEUE_AFTER"))
if err != nil {
requeueAfter = 30
}

if !instance.IsSubmitted() {
r.Recorder.Event(&instance, corev1.EventTypeNormal, "Submitting", "starting resource reconciliation for AzureSqlDatabase")
if err := r.reconcileExternal(&instance); err != nil {
Expand All @@ -99,7 +105,7 @@ func (r *AzureSqlDatabaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
if azerr, ok := err.(*errhelp.AzureError); ok {
if helpers.ContainsString(catch, azerr.Type) {
log.Info("Got ignorable error", "type", azerr.Type)
return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil
return ctrl.Result{Requeue: true, RequeueAfter: time.Duration(requeueAfter) * time.Second}, nil
}
}
return ctrl.Result{}, fmt.Errorf("error reconciling azure sql database in azure: %v", err)
Expand All @@ -126,13 +132,6 @@ func (r *AzureSqlDatabaseReconciler) reconcileExternal(instance *azurev1alpha1.A
dbName := instance.ObjectMeta.Name
dbEdition := instance.Spec.Edition

sdkClient := sql.GoSDKClient{
Ctx: ctx,
ResourceGroupName: groupName,
ServerName: server,
Location: location,
}

azureSqlDatabaseProperties := sql.SQLDatabaseProperties{
DatabaseName: dbName,
Edition: dbEdition,
Expand All @@ -159,11 +158,11 @@ func (r *AzureSqlDatabaseReconciler) reconcileExternal(instance *azurev1alpha1.A
}

// write information back to instance
if updateerr := r.Update(ctx, instance); updateerr != nil {
if updateerr := r.Status().Update(ctx, instance); updateerr != nil {
r.Recorder.Event(instance, corev1.EventTypeWarning, "Failed", "Unable to update instance")
}

_, err = sdkClient.CreateOrUpdateDB(azureSqlDatabaseProperties)
_, err = r.ResourceClient.CreateOrUpdateDB(ctx, groupName, location, server, azureSqlDatabaseProperties)
if err != nil {
if errhelp.IsAsynchronousOperationNotComplete(err) || errhelp.IsGroupNotFound(err) {
r.Log.Info("Async operation not complete or group not found")
Expand All @@ -176,7 +175,7 @@ func (r *AzureSqlDatabaseReconciler) reconcileExternal(instance *azurev1alpha1.A
return errhelp.NewAzureError(err)
}

_, err = sdkClient.GetDB(dbName)
_, err = r.ResourceClient.GetDB(ctx, groupName, server, dbName)
if err != nil {
return errhelp.NewAzureError(err)
}
Expand All @@ -193,21 +192,12 @@ func (r *AzureSqlDatabaseReconciler) reconcileExternal(instance *azurev1alpha1.A

func (r *AzureSqlDatabaseReconciler) deleteExternal(instance *azurev1alpha1.AzureSqlDatabase) error {
ctx := context.Background()
location := instance.Spec.Location
groupName := instance.Spec.ResourceGroup
server := instance.Spec.Server
dbName := instance.ObjectMeta.Name

// create the Go SDK client with relevant info
sdk := sql.GoSDKClient{
Ctx: ctx,
ResourceGroupName: groupName,
ServerName: server,
Location: location,
}

r.Log.Info(fmt.Sprintf("deleting external resource: group/%s/server/%s/database/%s"+groupName, server, dbName))
_, err := sdk.DeleteDB(dbName)
_, err := r.ResourceClient.DeleteDB(ctx, groupName, server, dbName)
if err != nil {
if errhelp.IsStatusCode204(err) {
r.Recorder.Event(instance, corev1.EventTypeWarning, "DoesNotExist", "Resource to delete does not exist")
Expand All @@ -220,13 +210,3 @@ func (r *AzureSqlDatabaseReconciler) deleteExternal(instance *azurev1alpha1.Azur
r.Recorder.Event(instance, corev1.EventTypeNormal, "Deleted", dbName+" deleted")
return nil
}

func (r *AzureSqlDatabaseReconciler) addFinalizer(instance *azurev1alpha1.AzureSqlDatabase) error {
helpers.AddFinalizer(instance, azureSQLDatabaseFinalizerName)
err := r.Update(context.Background(), instance)
if err != nil {
return fmt.Errorf("failed to update finalizer: %v", err)
}
r.Recorder.Event(instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("finalizer %s added", azureSQLDatabaseFinalizerName))
return nil
}
36 changes: 36 additions & 0 deletions controllers/azuresqldatabase_controller_finalizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2019 microsoft.

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.
*/

package controllers

import (
"context"
"fmt"

azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1"
helpers "github.com/Azure/azure-service-operator/pkg/helpers"
)

const AzureSQLDatabaseFinalizerName = "azuresqldatabase.finalizers.azure.com"

func (r *AzureSqlDatabaseReconciler) addFinalizer(instance *azurev1alpha1.AzureSqlDatabase) error {
helpers.AddFinalizer(instance, AzureSQLDatabaseFinalizerName)
if updateerr := r.Update(context.Background(), instance); updateerr != nil {
r.Recorder.Event(instance, "Warning", "Failed", "Failed to update finalizer")
}
r.Recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("finalizer %s added", AzureSQLDatabaseFinalizerName))
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for future reference...we have some generic functions to replace these in the helper lib.

120 changes: 120 additions & 0 deletions controllers/azuresqldatabase_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright 2019 microsoft.

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.
*/

package controllers

import (
"context"

azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1"

helpers "github.com/Azure/azure-service-operator/pkg/helpers"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

var _ = Describe("AzureSqlDatabase Controller", func() {

var rgName string
var rgLocation string
//var sqlName string

BeforeEach(func() {
// Add any setup steps that needs to be executed before each test
rgName = tc.resourceGroupName
rgLocation = tc.resourceGroupLocation
})

AfterEach(func() {
// Add any teardown steps that needs to be executed after each test
})

// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.

Context("Create and Delete", func() {
It("should create and delete sql database in k8s", func() {

randomName := helpers.RandomString(10)
sqlServerName := "t-sqlserver-dev-" + randomName
sqlDatabaseName := "t-sqldatabase-dev-" + randomName

var err error

// Create the SqlServer object and expect the Reconcile to be created
sqlServerInstance := &azurev1alpha1.AzureSqlServer{
ObjectMeta: metav1.ObjectMeta{
Name: sqlServerName,
Namespace: "default",
},
Spec: azurev1alpha1.AzureSqlServerSpec{
Location: rgLocation,
ResourceGroup: rgName,
},
}

err = tc.k8sClient.Create(context.Background(), sqlServerInstance)
Expect(err).NotTo(HaveOccurred())

// Create the SqlDatabase object and expect the Reconcile to be created
sqlDatabaseInstance := &azurev1alpha1.AzureSqlDatabase{
ObjectMeta: metav1.ObjectMeta{
Name: sqlDatabaseName,
Namespace: "default",
},
Spec: azurev1alpha1.AzureSqlDatabaseSpec{
Location: rgLocation,
ResourceGroup: rgName,
Server: sqlServerName,
Edition: 0,
},
}

err = tc.k8sClient.Create(context.Background(), sqlDatabaseInstance)
Expect(apierrors.IsInvalid(err)).To(Equal(false))
Expect(err).NotTo(HaveOccurred())

sqlDatabaseNamespacedName := types.NamespacedName{Name: sqlDatabaseName, Namespace: "default"}

Eventually(func() bool {
_ = tc.k8sClient.Get(context.Background(), sqlDatabaseNamespacedName, sqlDatabaseInstance)
return helpers.HasFinalizer(sqlDatabaseInstance, AzureSQLDatabaseFinalizerName)
}, tc.timeout,
).Should(BeTrue())

Eventually(func() bool {
_ = tc.k8sClient.Get(context.Background(), sqlDatabaseNamespacedName, sqlDatabaseInstance)
return sqlDatabaseInstance.IsSubmitted()
}, tc.timeout,
).Should(BeTrue())

err = tc.k8sClient.Delete(context.Background(), sqlDatabaseInstance)
Expect(err).NotTo(HaveOccurred())

Eventually(func() bool {
_ = tc.k8sClient.Get(context.Background(), sqlDatabaseNamespacedName, sqlDatabaseInstance)
return helpers.IsBeingDeleted(sqlDatabaseInstance)
}, tc.timeout,
).Should(BeTrue())

})
})
})
Loading