Skip to content

Commit

Permalink
Add more tests for backupcronjob and add namespace when creating backup
Browse files Browse the repository at this point in the history
  • Loading branch information
AMecea committed Dec 19, 2018
1 parent 8cce817 commit adb093d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 22 deletions.
64 changes: 64 additions & 0 deletions pkg/controller/internal/testutil/backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2018 Pressinfra SRL
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.
*/

// nolint: golint, errcheck
package testutil

import (
"context"

. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
gomegatypes "github.com/onsi/gomega/types"

core "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

api "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1"
)

// ListAllBackupsFn returns a helper function that can be used with gomega
// Eventually and Consistently
func ListAllBackupsFn(c client.Client, options *client.ListOptions) func() []api.MysqlBackup {
return func() []api.MysqlBackup {
backups := &api.MysqlBackupList{}
c.List(context.TODO(), options, backups)
return backups.Items
}
}

// BackupHaveCondition is a helper func that returns a matcher to check for an
// existing condition in condition list list
func BackupHaveCondition(condType api.BackupConditionType, status core.ConditionStatus) gomegatypes.GomegaMatcher {
return PointTo(MatchFields(IgnoreExtras, Fields{
"Status": MatchFields(IgnoreExtras, Fields{
"Conditions": ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(condType),
"Status": Equal(status),
})),
}),
}))
}

// BackupForCluster is gomega matcher that matches a backup which is for given
// cluster
func BackupForCluster(cluster *api.MysqlCluster) gomegatypes.GomegaMatcher {
return MatchFields(IgnoreExtras, Fields{
"Spec": MatchFields(IgnoreExtras, Fields{
"ClusterName": Equal(cluster.Name),
}),
})
}
18 changes: 0 additions & 18 deletions pkg/controller/internal/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,15 @@ import (
"time"

g "github.com/onsi/gomega"
gs "github.com/onsi/gomega/gstruct"
gomegatypes "github.com/onsi/gomega/types"

// loggging
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

core "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

api "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1"
)

const (
Expand All @@ -53,19 +48,6 @@ func DrainChan(requests <-chan reconcile.Request) {
}
}

// BackupHaveCondition is a helper func that returns a matcher to check for an
// existing condition in condition list list
func BackupHaveCondition(condType api.BackupConditionType, status core.ConditionStatus) gomegatypes.GomegaMatcher {
return gs.PointTo(gs.MatchFields(gs.IgnoreExtras, gs.Fields{
"Status": gs.MatchFields(gs.IgnoreExtras, gs.Fields{
"Conditions": g.ContainElement(gs.MatchFields(gs.IgnoreExtras, gs.Fields{
"Type": g.Equal(condType),
"Status": g.Equal(status),
})),
}),
}))
}

// NewTestLogger returns a logger good for tests
func NewTestLogger(w io.Writer, options ...zap.Option) logr.Logger {
encoderCfg := zapcore.EncoderConfig{
Expand Down
7 changes: 5 additions & 2 deletions pkg/controller/mysqlbackupcron/job_backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func (j job) Run() {
var err error
cluster := &api.MysqlBackup{
ObjectMeta: metav1.ObjectMeta{
Name: backupName,
Name: backupName,
Namespace: j.Namespace,
Labels: map[string]string{
"recurrent": "true",
},
Expand All @@ -89,14 +90,16 @@ func (j job) Run() {
if err = j.c.Create(context.TODO(), cluster); err == nil {
break
}
log.V(1).Info("failed to create backup", "backup", backupName, "error", err)

if tries > 5 {
log.Error(err, "fail to create backup, max tries exeded",
"cluster", j.Name, "retries", tries, "backup", backupName)
return false
}

log.Info("failed to create backup, retring", "backup", backupName,
"error", err, "tries", tries)

time.Sleep(5 * time.Second)
tries++
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"

"github.com/presslabs/mysql-operator/pkg/apis"
"github.com/presslabs/mysql-operator/pkg/controller/internal/testutil"
)

var cfg *rest.Config
Expand All @@ -43,6 +45,8 @@ func TestMysqlBackupController(t *testing.T) {
var _ = BeforeSuite(func() {
var err error

logf.SetLogger(testutil.NewTestLogger(GinkgoWriter))

t = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
Expand Down
45 changes: 43 additions & 2 deletions pkg/controller/mysqlbackupcron/mysqlbackupcron_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ import (
cronpkg "github.com/wgliang/cron"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

api "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1"
"github.com/presslabs/mysql-operator/pkg/controller/internal/testutil"
)

const timeout = time.Second * 2
Expand All @@ -56,8 +58,13 @@ var _ = Describe("MysqlBackupCron controller", func() {
var recFn reconcile.Reconciler
cron = cronpkg.New()

// start the cron here instead of using manager to start it because of a
// DATA RACE happens when in Start() and Entris() methods.
// Expect(mgr.Add(sscron)).To(Succeed())
cron.Start()

mgr, err := manager.New(cfg, manager.Options{})
Expect(err).NotTo(HaveOccurred())
Expect(err).To(Succeed())
c = mgr.GetClient()

recFn, requests = SetupTestReconcile(newReconciler(mgr, cron))
Expand Down Expand Up @@ -95,7 +102,7 @@ var _ = Describe("MysqlBackupCron controller", func() {
Replicas: &two,
SecretName: "a-secret",

BackupSchedule: "* * * * *",
BackupSchedule: "0 0 0 * *",
BackupSecretName: "a-backup-secret",
BackupURL: "gs://bucket/",
},
Expand Down Expand Up @@ -177,6 +184,40 @@ var _ = Describe("MysqlBackupCron controller", func() {
}),
}))))
})

When("backup is executed once per second", func() {
var (
timeout = 5 * time.Second
)

BeforeEach(func() {
// update cluster scheduler to run every second
cluster.Spec.BackupSchedule = "* * * * * *"
Expect(c.Update(context.TODO(), cluster)).To(Succeed())
})

AfterEach(func() {
// delete all created backups
lo := &client.ListOptions{}
for _, b := range testutil.ListAllBackupsFn(c, lo)() {
c.Delete(context.TODO(), &b)
}
})

It("should create the mysqlbackup", func() {
lo := &client.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{
"recurrent": "true",
}),
Namespace: cluster.Namespace,
}
Eventually(testutil.ListAllBackupsFn(c, lo), timeout).Should(
ContainElement(testutil.BackupForCluster(cluster)))

// it should have only a backup created
Consistently(testutil.ListAllBackupsFn(c, lo), "2s").Should(HaveLen(1))
})
})
})
})

Expand Down

0 comments on commit adb093d

Please sign in to comment.