Skip to content

Commit

Permalink
Dynamic registration of Kanister functions with multiple versions (#371)
Browse files Browse the repository at this point in the history
* Changes to register functions with versions

* Add unit test

* Func signature changes in tests

* Add version to actionsets in e2e tests

* Address review comments and add more unit tests
  • Loading branch information
pavannd1 authored and mergify[bot] committed Oct 25, 2019
1 parent 2733489 commit 0f922ac
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 36 deletions.
2 changes: 1 addition & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ func (c *Controller) runAction(ctx context.Context, as *crv1alpha1.ActionSet, aI
if err != nil {
return err
}
phases, err := kanister.GetPhases(*bp, action.Name, *tp)
phases, err := kanister.GetPhases(*bp, action.Name, action.Version, *tp)
if err != nil {
return err
}
Expand Down
42 changes: 38 additions & 4 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import (
"github.com/pkg/errors"
. "gopkg.in/check.v1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"

kanister "github.com/kanisterio/kanister/pkg"
crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1"
"github.com/kanisterio/kanister/pkg/client/clientset/versioned/scheme"
crclientv1alpha1 "github.com/kanisterio/kanister/pkg/client/clientset/versioned/typed/cr/v1alpha1"
Expand Down Expand Up @@ -268,42 +269,72 @@ func (s *ControllerSuite) TestExecActionSet(c *C) {
funcNames []string
args [][]string
name string
version string
}{
{
funcNames: []string{testutil.WaitFuncName},
name: "WaitFunc",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.WaitFuncName, testutil.WaitFuncName},
name: "WaitWait",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.FailFuncName},
name: "FailFunc",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.WaitFuncName, testutil.FailFuncName},
name: "WaitFail",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.FailFuncName, testutil.WaitFuncName},
name: "FailWait",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.ArgFuncName},
name: "ArgFunc",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.ArgFuncName, testutil.FailFuncName},
name: "ArgFail",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.OutputFuncName},
name: "OutputFunc",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.CancelFuncName},
name: "CancelFunc",
version: kanister.DefaultVersion,
},
{
funcNames: []string{testutil.ArgFuncName},
name: "ArgFuncVersion",
version: testutil.TestVersion,
},
{
funcNames: []string{testutil.ArgFuncName},
name: "ArgFuncVersionFallback",
version: "v1.2.3",
},
{
funcNames: []string{testutil.ArgFuncName},
name: "ArgFuncNoActionSetVersion",
version: "",
},
{
funcNames: []string{testutil.VersionMismatchFuncName},
name: "VersionMismatchFunc",
version: "v1.2.3",
},
} {
var err error
Expand All @@ -324,7 +355,7 @@ func (s *ControllerSuite) TestExecActionSet(c *C) {
}

// Add an actionset that references that blueprint.
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), pok, n, s.namespace)
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), pok, n, s.namespace, tc.version)
as = testutil.ActionSetWithConfigMap(as, s.confimap.GetName())
as, err = s.crCli.ActionSets(s.namespace).Create(as)
c.Assert(err, IsNil, Commentf("Failed case: %s", tc.name))
Expand Down Expand Up @@ -352,6 +383,9 @@ func (s *ControllerSuite) TestExecActionSet(c *C) {
c.Assert(err, IsNil)
c.Assert(testutil.CancelFuncOut().Error(), DeepEquals, "context canceled")
cancel = true
case testutil.VersionMismatchFuncName:
final = crv1alpha1.StateFailed
c.Assert(err, IsNil)
}
}

Expand Down Expand Up @@ -442,7 +476,7 @@ func (s *ControllerSuite) TestPhaseOutputAsArtifact(c *C) {
c.Assert(err, IsNil)

// Add an actionset that references that blueprint.
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), "Deployment", s.deployment.GetName(), s.namespace)
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), "Deployment", s.deployment.GetName(), s.namespace, kanister.DefaultVersion)
as = testutil.ActionSetWithConfigMap(as, s.confimap.GetName())
as, err = s.crCli.ActionSets(s.namespace).Create(as)
c.Assert(err, IsNil)
Expand Down Expand Up @@ -472,7 +506,7 @@ func (s *ControllerSuite) TestRenderArtifactsFailure(c *C) {
c.Assert(err, IsNil)

// Add an actionset that references that blueprint.
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), "Deployment", s.deployment.GetName(), s.namespace)
as := testutil.NewTestActionSet(s.namespace, bp.GetName(), "Deployment", s.deployment.GetName(), s.namespace, kanister.DefaultVersion)
as = testutil.ActionSetWithConfigMap(as, s.confimap.GetName())
as, err = s.crCli.ActionSets(s.namespace).Create(as)
c.Assert(err, IsNil)
Expand Down
2 changes: 1 addition & 1 deletion pkg/function/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ func (s *DataSuite) TestCopyData(c *C) {
}

func runAction(c *C, bp crv1alpha1.Blueprint, action string, tp *param.TemplateParams) map[string]interface{} {
phases, err := kanister.GetPhases(bp, action, *tp)
phases, err := kanister.GetPhases(bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
out := make(map[string]interface{})
for _, p := range phases {
Expand Down
2 changes: 1 addition & 1 deletion pkg/function/e2e_volume_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func (s *VolumeSnapshotTestSuite) TestVolumeSnapshot(c *C) {
actions := []string{"backup", "restore", "delete"}
bp := newVolumeSnapshotBlueprint()
for _, action := range actions {
phases, err := kanister.GetPhases(*bp, action, *s.tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *s.tp)
c.Assert(err, IsNil)
for _, p := range phases {
c.Assert(param.InitPhaseParams(ctx, s.cli, s.tp, p.Name(), p.Objects()), IsNil)
Expand Down
6 changes: 3 additions & 3 deletions pkg/function/kube_exec_all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"fmt"

. "gopkg.in/check.v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -129,7 +129,7 @@ func (s *KubeExecAllTest) TestKubeExecAllDeployment(c *C) {

action := "echo"
bp := newExecAllBlueprint(kind)
phases, err := kanister.GetPhases(*bp, action, *tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(ctx, *bp, action, *tp)
Expand Down Expand Up @@ -163,7 +163,7 @@ func (s *KubeExecAllTest) TestKubeExecAllStatefulSet(c *C) {

action := "echo"
bp := newExecAllBlueprint(kind)
phases, err := kanister.GetPhases(*bp, action, *tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(ctx, *bp, action, *tp)
Expand Down
2 changes: 1 addition & 1 deletion pkg/function/kube_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func (s *KubeExecTest) TestKubeExec(c *C) {

action := "echo"
bp := newKubeExecBlueprint()
phases, err := kanister.GetPhases(*bp, action, *tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(context.Background(), *bp, action, *tp)
Expand Down
4 changes: 2 additions & 2 deletions pkg/function/kube_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"time"

. "gopkg.in/check.v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
sp "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -153,7 +153,7 @@ func (s *KubeTaskSuite) TestKubeTask(c *C) {
},
} {

phases, err := kanister.GetPhases(*tc.bp, action, tp)
phases, err := kanister.GetPhases(*tc.bp, action, kanister.DefaultVersion, tp)
c.Assert(err, IsNil)
c.Assert(phases, HasLen, len(tc.outs))
for i, p := range phases {
Expand Down
4 changes: 2 additions & 2 deletions pkg/function/prepare_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"fmt"

. "gopkg.in/check.v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"

Expand Down Expand Up @@ -144,7 +144,7 @@ func (s *PrepareDataSuite) TestPrepareData(c *C) {
}
action := "test"
bp := newPrepareDataBlueprint(kind, createdPVC.Name)
phases, err := kanister.GetPhases(*bp, action, tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(ctx, *bp, action, tp)
Expand Down
6 changes: 3 additions & 3 deletions pkg/function/scale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"fmt"

. "gopkg.in/check.v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -159,7 +159,7 @@ func (s *ScaleSuite) TestScaleDeployment(c *C) {
tp, err := param.New(ctx, s.cli, fake.NewSimpleDynamicClient(k8sscheme.Scheme, d), s.crCli, as)
c.Assert(err, IsNil)
bp := newScaleBlueprint(kind)
phases, err := kanister.GetPhases(*bp, action, *tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(context.Background(), *bp, action, *tp)
Expand Down Expand Up @@ -208,7 +208,7 @@ func (s *ScaleSuite) TestScaleStatefulSet(c *C) {
tp, err := param.New(ctx, s.cli, fake.NewSimpleDynamicClient(k8sscheme.Scheme, ss), s.crCli, as)
c.Assert(err, IsNil)
bp := newScaleBlueprint(kind)
phases, err := kanister.GetPhases(*bp, action, *tp)
phases, err := kanister.GetPhases(*bp, action, kanister.DefaultVersion, *tp)
c.Assert(err, IsNil)
for _, p := range phases {
_, err = p.Exec(context.Background(), *bp, action, *tp)
Expand Down
37 changes: 32 additions & 5 deletions pkg/kanister.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ import (
"context"
"sync"

"github.com/Masterminds/semver"
"github.com/pkg/errors"

"github.com/kanisterio/kanister/pkg/param"
)

const (
DefaultVersion = "v0.0.0"
)

var (
funcMu sync.RWMutex
funcs = make(map[string]Func)
funcs = make(map[string]map[semver.Version]Func)
)

// Func allows custom actions to be executed.
Expand All @@ -35,16 +40,38 @@ type Func interface {
Exec(context.Context, param.TemplateParams, map[string]interface{}) (map[string]interface{}, error)
}

// Register allows Funcs to be references by User Defined YAMLs
// Register allows Funcs to be referenced by User Defined YAMLs
func Register(f Func) error {
version := *semver.MustParse(DefaultVersion)
funcMu.Lock()
defer funcMu.Unlock()
if f == nil {
return errors.Errorf("kanister: Cannot register nil function")
}
if _, dup := funcs[f.Name()]; dup {
panic("kanister: Register called twice for function " + f.Name())
if _, ok := funcs[f.Name()][version]; ok {
panic("kanister: Register called twice for function " + f.Name() + " with version " + DefaultVersion)
}
if _, ok := funcs[f.Name()]; !ok {
funcs[f.Name()] = make(map[semver.Version]Func)
}
funcs[f.Name()][version] = f
return nil
}

// RegisterVersion allows Kanister Functions to be registered with the given version
func RegisterVersion(f Func, v string) error {
version := *semver.MustParse(v)
funcMu.Lock()
defer funcMu.Unlock()
if f == nil {
return errors.Errorf("kanister: Cannot register nil function")
}
if _, ok := funcs[f.Name()][version]; ok {
panic("kanister: Register called twice for function " + f.Name() + " with version " + v)
}
if _, ok := funcs[f.Name()]; !ok {
funcs[f.Name()] = make(map[semver.Version]Func)
}
funcs[f.Name()] = f
funcs[f.Name()][version] = f
return nil
}
Loading

0 comments on commit 0f922ac

Please sign in to comment.