-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding a Kanister function to create CSI VolumeSnapshot (#1163)
* Added new function CreateCSISnapshot * Fixed the instantiated values for kubeCli and dynCli * Added waitForReady=true as the default * Added apiGroup snapshot.storage.k8s.io in cluster-role for kanister-operator * Added documentation for Create CSI VolumeSnapshot Func * Changed namespace to a required arg * Rasults of running gofmt * Reverting changes to return type of snapshotter.Create() * Changed snapShotter -> snapshotter * grouped imports and added the comment for 'waitForReady' * Updates suggested in docs/functions.rst * Changed snapshot name argument to optional argument and added a default value * Changes suggested in reviewer comments * gofmt update * gofmt update * Updated const comments and function description in documentation * Declared and used const for outputartifact keys * Minor changes in docs/functions.rst * Updated the rules in clusterrole * added asterisk again in helm/kanister-operator/templates/rbac.yaml * Updated rules in clusterrole to check Travis CI failure * Created a test suite for CreateCSISanpshot func * Updated the testcase * Resolved the error in testcase * Updated the create snapshot testcase * removed AddKnownTypeWithName * Updated the CreateCSISnapshot testcase * golang-ci: fixed whitespace * Reviewer comments * Updated the testcase for CreateCSiSnapshot function * Fixed RBAC YAML * Added default context timeout * Fixed the time.Minute issue * Updated the defer cancel() * Added a custom error for create * Returned the custom error * Updated the error msg * Removed the timeout code snippet * Removed unnecessary const for default timeout and import for time package * Added the DeadlineExceeded error check and corrected the imports * Added the wrapped error message * Updated the wrapped error msg Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
2de6893
commit 575144e
Showing
4 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright 2022 The Kanister Authors. | ||
// | ||
// 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 function | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
"k8s.io/apimachinery/pkg/util/rand" | ||
|
||
kanister "github.com/kanisterio/kanister/pkg" | ||
"github.com/kanisterio/kanister/pkg/kube" | ||
"github.com/kanisterio/kanister/pkg/kube/snapshot" | ||
"github.com/kanisterio/kanister/pkg/param" | ||
) | ||
|
||
func init() { | ||
_ = kanister.Register(&createCSISnapshotFunc{}) | ||
} | ||
|
||
var ( | ||
_ kanister.Func = (*createCSISnapshotFunc)(nil) | ||
) | ||
|
||
const ( | ||
// CreateCSIVolumeSnapshotFuncName gives the name of the function | ||
CreateCSISnapshotFuncName = "CreateCSISnapshot" | ||
// CreateCSISnapshotNameArg provides name of the new VolumeSnapshot | ||
CreateCSISnapshotNameArg = "name" | ||
// CreateCSISnapshotPVCNameArg gives the name of the captured PVC | ||
CreateCSISnapshotPVCNameArg = "pvc" | ||
// CreateCSISnapshotNamespaceArg mentions the namespace of the captured PVC | ||
CreateCSISnapshotNamespaceArg = "namespace" | ||
// CreateCSISnapshotSnapshotClassArg specifies the name of the VolumeSnapshotClass | ||
CreateCSISnapshotSnapshotClassArg = "snapshotClass" | ||
// CreateCSISnapshotLabelsArg has labels that are to be added to the new VolumeSnapshot | ||
CreateCSISnapshotLabelsArg = "labels" | ||
// CreateCSISnapshotRestoreSizeArg gives the storage size required for PV/PVC restoration | ||
CreateCSISnapshotRestoreSizeArg = "restoreSize" | ||
// CreateCSISnapshotSnapshotContentNameArg provides the name of dynamically provisioned VolumeSnapshotContent | ||
CreateCSISnapshotSnapshotContentNameArg = "snapshotContent" | ||
) | ||
|
||
type createCSISnapshotFunc struct{} | ||
|
||
func (*createCSISnapshotFunc) Name() string { | ||
return CreateCSISnapshotFuncName | ||
} | ||
|
||
func (*createCSISnapshotFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) { | ||
var snapshotClass string | ||
var labels map[string]string | ||
var name, pvc, namespace string | ||
if err := Arg(args, CreateCSISnapshotPVCNameArg, &pvc); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, CreateCSISnapshotNamespaceArg, &namespace); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, CreateCSISnapshotSnapshotClassArg, &snapshotClass); err != nil { | ||
return nil, err | ||
} | ||
if err := OptArg(args, CreateCSISnapshotNameArg, &name, defaultSnapshotName(pvc, 20)); err != nil { | ||
This comment has been minimized.
Sorry, something went wrong. |
||
return nil, err | ||
} | ||
if err := OptArg(args, CreateCSISnapshotLabelsArg, &labels, map[string]string{}); err != nil { | ||
return nil, err | ||
} | ||
|
||
kubeCli, err := kube.NewClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
dynCli, err := kube.NewDynamicClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
snapshotter, err := snapshot.NewSnapshotter(kubeCli, dynCli) | ||
if err != nil { | ||
if errors.Is(context.DeadlineExceeded, err) { | ||
timeoutMsg := "SnapshotContent not provisioned within given timeout. Please check if CSI driver is installed correctly and supports VolumeSnapshot feature" | ||
return nil, errors.Wrap(err, timeoutMsg) | ||
} | ||
return nil, err | ||
} | ||
// waitForReady is set to true by default because snapshot information is needed as output artifacts | ||
waitForReady := true | ||
if err := snapshotter.Create(ctx, name, namespace, pvc, &snapshotClass, waitForReady, labels); err != nil { | ||
return nil, err | ||
} | ||
vs, err := snapshotter.Get(ctx, name, namespace) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
snapshotInfo := map[string]interface{}{ | ||
CreateCSISnapshotNameArg: name, | ||
CreateCSISnapshotPVCNameArg: pvc, | ||
CreateCSISnapshotNamespaceArg: namespace, | ||
CreateCSISnapshotRestoreSizeArg: vs.Status.RestoreSize.String(), | ||
CreateCSISnapshotSnapshotContentNameArg: vs.Status.BoundVolumeSnapshotContentName, | ||
} | ||
return snapshotInfo, nil | ||
} | ||
|
||
func (*createCSISnapshotFunc) RequiredArgs() []string { | ||
return []string{ | ||
CreateCSISnapshotPVCNameArg, | ||
CreateCSISnapshotNamespaceArg, | ||
CreateCSISnapshotSnapshotClassArg, | ||
} | ||
} | ||
|
||
// defaultSnapshotName generates snapshot name using pvcName-snapshot-randomValue | ||
func defaultSnapshotName(pvcName string, len int) string { | ||
return fmt.Sprintf("%s-snapshot-%s", pvcName, rand.String(len)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright 2022 The Kanister Authors. | ||
// | ||
// 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 function | ||
|
||
import ( | ||
"context" | ||
|
||
. "gopkg.in/check.v1" | ||
v1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
dynfake "k8s.io/client-go/dynamic/fake" | ||
"k8s.io/client-go/kubernetes/fake" | ||
|
||
"github.com/kanisterio/kanister/pkg/kube/snapshot" | ||
) | ||
|
||
const ( | ||
// CreateCSISnapshotTestNamespace is the namespace where testing is done | ||
CreateCSISnapshotTestNamespace = "test-create-csi-snapshot" | ||
// CreateCSISnapshotPVCName is the name of the PVC that will be captured | ||
CreateCSISnapshotPVCName = "test-pvc" | ||
// CreateCSISnapshotSnapshotName is the name of the snapshot | ||
CreateCSISnapshotSnapshotName = "test-snapshot" | ||
// CreateCSISnapshotSnapshotClass is the fake snapshot class | ||
CreateCSISnapshotSnapshotClass = "test-snapshot-class" | ||
// CreateCSISnapshotStorageClass is the fake storage class | ||
CreateCSISnapshotStorageClass = "test-storage-class" | ||
) | ||
|
||
type CreateCSISnapshotTestSuite struct { | ||
snapName string | ||
pvcName string | ||
namespace string | ||
volumeSnapshotClass string | ||
storageClass string | ||
} | ||
|
||
var _ = Suite(&CreateCSISnapshotTestSuite{}) | ||
|
||
func (testSuite *CreateCSISnapshotTestSuite) SetUpSuite(c *C) { | ||
testSuite.volumeSnapshotClass = CreateCSISnapshotSnapshotClass | ||
testSuite.storageClass = CreateCSISnapshotStorageClass | ||
testSuite.pvcName = CreateCSISnapshotPVCName | ||
testSuite.snapName = CreateCSISnapshotSnapshotName | ||
testSuite.namespace = CreateCSISnapshotTestNamespace | ||
} | ||
|
||
func (testSuite *CreateCSISnapshotTestSuite) TestCreateCSISnapshot(c *C) { | ||
for _, apiResourceList := range []*metav1.APIResourceList{ | ||
{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "VolumeSnapshot", | ||
APIVersion: "v1alpha1", | ||
}, | ||
GroupVersion: "snapshot.storage.k8s.io/v1alpha1", | ||
}, | ||
{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "VolumeSnapshot", | ||
APIVersion: "v1beta1", | ||
}, | ||
GroupVersion: "snapshot.storage.k8s.io/v1beta1", | ||
}, | ||
{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "VolumeSnapshot", | ||
APIVersion: "v1", | ||
}, | ||
GroupVersion: "snapshot.storage.k8s.io/v1", | ||
}, | ||
} { | ||
fakeCli := fake.NewSimpleClientset() | ||
fakeCli.Resources = []*metav1.APIResourceList{apiResourceList} | ||
|
||
_, err := fakeCli.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testSuite.namespace}}, metav1.CreateOptions{}) | ||
c.Assert(err, IsNil) | ||
|
||
scheme := runtime.NewScheme() | ||
fakeSnapshotter, err := snapshot.NewSnapshotter(fakeCli, dynfake.NewSimpleDynamicClient(scheme)) | ||
c.Assert(err, IsNil) | ||
|
||
_, err = fakeCli.CoreV1().PersistentVolumeClaims(testSuite.namespace).Create(context.TODO(), getPVCManifest(testSuite.pvcName, testSuite.storageClass), metav1.CreateOptions{}) | ||
c.Assert(err, IsNil) | ||
|
||
err = fakeSnapshotter.Create(context.Background(), testSuite.snapName, testSuite.namespace, testSuite.pvcName, &testSuite.volumeSnapshotClass, false, nil) | ||
c.Assert(err, IsNil) | ||
|
||
vs, err := fakeSnapshotter.Get(context.Background(), testSuite.snapName, testSuite.namespace) | ||
c.Assert(err, IsNil) | ||
c.Assert(vs.Name, Equals, testSuite.snapName) | ||
|
||
err = fakeCli.CoreV1().Namespaces().Delete(context.Background(), testSuite.namespace, metav1.DeleteOptions{}) | ||
c.Assert(err, IsNil) | ||
} | ||
} | ||
|
||
func getPVCManifest(pvcName, storageClassName string) *v1.PersistentVolumeClaim { | ||
return &v1.PersistentVolumeClaim{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: pvcName, | ||
}, | ||
Spec: v1.PersistentVolumeClaimSpec{ | ||
StorageClassName: &storageClassName, | ||
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, | ||
Resources: v1.ResourceRequirements{ | ||
Requests: v1.ResourceList{ | ||
v1.ResourceStorage: resource.MustParse("1Gi"), | ||
}, | ||
}, | ||
}, | ||
} | ||
} |
I think
5
characters should be good enough here. It uses signed int64 I think which works for millions of combinations!