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

Dynamic registration of Kanister functions with multiple versions #371

Merged
merged 6 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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)
pavannd1 marked this conversation as resolved.
Show resolved Hide resolved
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