Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

Commit

Permalink
Merge pull request #201 from JohnStrunk/refactor3
Browse files Browse the repository at this point in the history
Create mover Interface to start isolating mover code
  • Loading branch information
openshift-merge-robot committed Jul 8, 2021
2 parents e5310c1 + 36bcffb commit 7b0980b
Show file tree
Hide file tree
Showing 46 changed files with 3,517 additions and 922 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ helm-lint: helm ## Lint Helm chart
.PHONY: test
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
TEST_ARGS ?= -progress -randomizeAllSpecs -randomizeSuites -slowSpecThreshold 30 -p -cover -coverprofile cover.out -outputdir .
TEST_PACKAGES ?= ./...
test: manifests generate lint helm-lint ginkgo ## Run tests.
mkdir -p ${ENVTEST_ASSETS_DIR}
test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh
source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); $(GINKGO) $(TEST_ARGS) ./...
source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); $(GINKGO) $(TEST_ARGS) $(TEST_PACKAGES)

.PHONY: test-e2e
test-e2e: kuttl ## Run e2e tests. Requires cluster w/ Scribe already installed
Expand Down
8 changes: 8 additions & 0 deletions api/v1alpha1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ const (
// reconciling the CR
ReconciledReasonError status.ConditionReason = "ReconcileError"
)

const (
ConditionSynchronizing status.ConditionType = "Synchronizing"
SynchronizingReasonSync status.ConditionReason = "SyncInProgress"
SynchronizingReasonSched status.ConditionReason = "WaitingForSchedule"
SynchronizingReasonManual status.ConditionReason = "WaitingForManual"
SynchronizingReasonCleanup status.ConditionReason = "CleaningUp"
)
3 changes: 3 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rules:
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
Expand All @@ -25,6 +26,7 @@ rules:
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
Expand Down Expand Up @@ -169,6 +171,7 @@ rules:
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
Expand Down
51 changes: 51 additions & 0 deletions controllers/mover/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2021 The Scribe authors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package mover

import (
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/client"

scribev1alpha1 "github.com/backube/scribe/api/v1alpha1"
)

// Catalog is the list of the available Builders for the controller to use when
// attempting to find an appropriate mover to service the RS/RD CR.
var Catalog []Builder

// Register should be called by each mover via an init function to register the
// mover w/ the main Scribe codebase.
func Register(builder Builder) {
Catalog = append(Catalog, builder)
}

// Builder is used to construct Mover instances for the different data
// mover types.
type Builder interface {
// FromSource attempts to construct a Mover from the provided
// ReplicationSource. If the RS does not reference the Builder's mover type,
// this function should return (nil, nil).
FromSource(client client.Client, logger logr.Logger,
source *scribev1alpha1.ReplicationSource) (Mover, error)

// FromDestination attempts to construct a Mover from the provided
// ReplicationDestination. If the RS does not reference the Builder's mover
// type, this function should return (nil, nil).
FromDestination(client client.Client, logger logr.Logger,
destination *scribev1alpha1.ReplicationDestination) (Mover, error)
}
32 changes: 32 additions & 0 deletions controllers/mover/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright 2021 The Scribe authors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

// Scribe data movers are created by implementing the interfaces in this
// package. Each data mover must implement the Builder interface that constructs
// instances of the Mover (interface) given a ReplicationSource or
// ReplicationDestination CR. These builders must be created and Register()-ed
// with the Catalog at startup time for that mover type to be available.
//
// When an RS or RD CR is reconciled, the Builders in the Catalog are tried in
// sequence. If one successfully returns a Mover, that mover is used to perform
// the reconcile.
//
// Movers implement the actual synchronization of data and return a Result from
// each invocation. When one of the Mover's functions returns Completed(), the
// operation (either synchronization or cleanup of a previous synchronization is
// considered to be completed).
package mover
91 changes: 91 additions & 0 deletions controllers/mover/mover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Copyright 2021 The Scribe authors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package mover

import (
"context"
"time"

v1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
)

// Mover is a common interface that all data movers implement
type Mover interface {
// The name of this data mover
Name() string

// Synchronize begins or continues a synchronization attempt. Attempts will
// continue at least until the Result indicates that the synchronization is
// complete. Must be idempotent.
Synchronize(ctx context.Context) (Result, error)

// Cleanup begins or continues the post-synchronization cleanup of temporary
// resources. Must be idempotent.
Cleanup(ctx context.Context) (Result, error)
}

// Result indicates the outcome of a synchronization attempt
type Result struct {
// Completed is set to true if the synchronization has completed. RetryAfter
// will be ignored.
Completed bool

// Image is the resulting data image (PVC or Snapshot) that has been created
// by the Synchronize() operation.
Image *v1.TypedLocalObjectReference

// RetryAfter is used to indicate whether synchronization should be
// explicitly retried, and when. Setting to nil (default) does not cause an
// explicit retry, but Synchronize() will be retried when a watched object
// is modified. Setting to 0 indicates an immediate retry. Other values
// provide a delay.
RetryAfter *time.Duration
}

// ReconcileResult converts a Result into controllerruntime's reconcile result
// structure
func (mr Result) ReconcileResult() ctrl.Result {
if mr.RetryAfter != nil {
return ctrl.Result{
Requeue: true,
RequeueAfter: *mr.RetryAfter,
}
}
return ctrl.Result{}
}

// InProgress result indicates that the requested operation is still ongoing,
// but it does not request an explicit requeueing.
func InProgress() Result { return Result{} }

// RetryAfter indicates the operation is ongoing and requests explicit
// requeueing after the provided duration.
func RetryAfter(s time.Duration) Result { return Result{RetryAfter: &s} }

// Complete indicates that the operation has completed.
func Complete() Result { return Result{Completed: true} }

// CompleteWithImage indicates that the operation has completed, and it provides
// the synchronized image to the controller.
func CompleteWithImage(image *v1.TypedLocalObjectReference) Result {
return Result{
Completed: true,
Image: image,
}
}
116 changes: 116 additions & 0 deletions controllers/mover/restic/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
Copyright 2021 The Scribe authors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package restic

import (
"flag"

"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/client"

scribev1alpha1 "github.com/backube/scribe/api/v1alpha1"
"github.com/backube/scribe/controllers/mover"
"github.com/backube/scribe/controllers/volumehandler"
)

// defaultResticContainerImage is the default container image for the restic
// data mover
const defaultResticContainerImage = "quay.io/backube/scribe-mover-restic:latest"

// resticContainerImage is the container image name of the restic data mover
var resticContainerImage string

type Builder struct{}

var _ mover.Builder = &Builder{}

func Register() {
flag.StringVar(&resticContainerImage, "restic-container-image",
defaultResticContainerImage, "The container image for the restic data mover")
mover.Register(&Builder{})
}

func (rb *Builder) FromSource(client client.Client, logger logr.Logger,
source *scribev1alpha1.ReplicationSource) (mover.Mover, error) {
// Only build if the CR belongs to us
if source.Spec.Restic == nil {
return nil, nil
}

// Create ReplicationSourceResticStatus to write restic status
if source.Status.Restic == nil {
source.Status.Restic = &scribev1alpha1.ReplicationSourceResticStatus{}
}

vh, err := volumehandler.NewVolumeHandler(
volumehandler.WithClient(client),
volumehandler.WithOwner(source),
volumehandler.FromSource(&source.Spec.Restic.ReplicationSourceVolumeOptions),
)
if err != nil {
return nil, err
}

return &Mover{
client: client,
logger: logger.WithValues("method", "Restic"),
owner: source,
vh: vh,
cacheAccessModes: source.Spec.Restic.CacheAccessModes,
cacheCapacity: source.Spec.Restic.CacheCapacity,
cacheStorageClassName: source.Spec.Restic.CacheStorageClassName,
repositoryName: source.Spec.Restic.Repository,
isSource: true,
paused: source.Spec.Paused,
mainPVCName: &source.Spec.SourcePVC,
pruneInterval: source.Spec.Restic.PruneIntervalDays,
retainPolicy: source.Spec.Restic.Retain,
sourceStatus: source.Status.Restic,
}, nil
}

func (rb *Builder) FromDestination(client client.Client, logger logr.Logger,
destination *scribev1alpha1.ReplicationDestination) (mover.Mover, error) {
// Only build if the CR belongs to us
if destination.Spec.Restic == nil {
return nil, nil
}

vh, err := volumehandler.NewVolumeHandler(
volumehandler.WithClient(client),
volumehandler.WithOwner(destination),
volumehandler.FromDestination(&destination.Spec.Restic.ReplicationDestinationVolumeOptions),
)
if err != nil {
return nil, err
}

return &Mover{
client: client,
logger: logger.WithValues("method", "Restic"),
owner: destination,
vh: vh,
cacheAccessModes: destination.Spec.Restic.CacheAccessModes,
cacheCapacity: destination.Spec.Restic.CacheCapacity,
cacheStorageClassName: destination.Spec.Restic.CacheStorageClassName,
repositoryName: destination.Spec.Restic.Repository,
isSource: false,
paused: destination.Spec.Paused,
mainPVCName: destination.Spec.Restic.DestinationPVC,
}, nil
}

0 comments on commit 7b0980b

Please sign in to comment.