-
Notifications
You must be signed in to change notification settings - Fork 154
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 restore CSI VolumeSnapshot (#1166)
* Added new function RestoreCSISnapshot * reviewer comments * Fixed failed to render outputartifact template 'snapshotInfo' issue * Updated const comments * Updated the return obj for the function * Added documentation for RestoreCSISnapshot function * Added test suite for RestoreCSISnapshot func * gofmt update * Reviewer comments * Updated the testcase for RestoreCSISnapshot function * Updated the createPVC method in testcase * Fixed RBAC YAML * Added validations for volumeMode and accessMode * Updated the logic to validate volume mode * Fixed the validateVolumeModeArg method * Added testcase for arg validation * gofmt updates * Added table driven testcases for validate methods * Removed multiple context.Background() calls and removed the validate method calls in main test case * style: Add whitespace in error messages * refactor: Update const names in test files * refactor: Update length of random alphanumeric suffix to 5 characters * refactor: Update argument validation methods * style: Fix typo in a comment * refactor: Update call to validate methods in restore unit test * refactor: Update restore test case as per reviewer comment * refactor: Update CreateCSiSnapshot function and the test case * refactor: Revert random alphanumeric string length * fix: gofmt error * refactor: Update CreateCSiSnapshot function and the test case * refactor: Update CreateCSiSnapshot function and the test case * refactor: Update CreateCSiSnapshot function and the test case * chore: Update signature of the restoreCSISnapshot method * refactor: Revert CreateCSISnapshot Func changes in this PR * refactor: Update create snapshot unit test * fix: CI failure * chore: Comment update * fix: golangci-lint fix Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
0362c2f
commit fe499ac
Showing
5 changed files
with
466 additions
and
29 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
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,201 @@ | ||
// 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" | ||
"errors" | ||
|
||
v1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
|
||
kanister "github.com/kanisterio/kanister/pkg" | ||
"github.com/kanisterio/kanister/pkg/kube" | ||
"github.com/kanisterio/kanister/pkg/param" | ||
) | ||
|
||
func init() { | ||
_ = kanister.Register(&restoreCSISnapshotFunc{}) | ||
} | ||
|
||
var ( | ||
_ kanister.Func = (*restoreCSISnapshotFunc)(nil) | ||
) | ||
|
||
const ( | ||
// Snapshot API Group | ||
SnapshotAPIGroup = "snapshot.storage.k8s.io" | ||
// RestoreCSISnapshotFuncName gives the name of the function | ||
RestoreCSISnapshotFuncName = "RestoreCSISnapshot" | ||
// RestoreCSISnapshotNameArg provides name of the VolumeSnapshot | ||
RestoreCSISnapshotNameArg = "name" | ||
// RestoreCSISnapshotPVCNameArg gives the name of the newly restored PVC | ||
RestoreCSISnapshotPVCNameArg = "pvc" | ||
// RestoreCSISnapshotNamespaceArg mentions the namespace of the newly restored PVC | ||
RestoreCSISnapshotNamespaceArg = "namespace" | ||
// RestoreCSISnapshotStorageClassArg specifies the name of the StorageClass | ||
RestoreCSISnapshotStorageClassArg = "storageClass" | ||
// RestoreCSISnapshotStorageRequestArg provides the storage size to be requested for PV in PVC | ||
RestoreCSISnapshotRestoreSizeArg = "restoreSize" | ||
// RestoreCSISnapshotAccessModesArg lists down the accessmodes for the underlying PV | ||
RestoreCSISnapshotAccessModesArg = "accessModes" | ||
// RestoreCSISnapshotLabelsArg has labels that will be added to the newly restored PVC | ||
RestoreCSISnapshotLabelsArg = "labels" | ||
// RestoreCSISnapshotVolumeModeArg defines mode of volume | ||
RestoreCSISnapshotVolumeModeArg = "volumeMode" | ||
) | ||
|
||
type restoreCSISnapshotFunc struct{} | ||
|
||
type restoreCSISnapshotArgs struct { | ||
Name string | ||
PVC string | ||
Namespace string | ||
StorageClass string | ||
RestoreSize *resource.Quantity | ||
AccessModes []v1.PersistentVolumeAccessMode | ||
Labels map[string]string | ||
VolumeMode v1.PersistentVolumeMode | ||
} | ||
|
||
func (*restoreCSISnapshotFunc) Name() string { | ||
return RestoreCSISnapshotFuncName | ||
} | ||
|
||
func (*restoreCSISnapshotFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) { | ||
var restoreSize string | ||
var restoreArgs restoreCSISnapshotArgs | ||
if err := Arg(args, RestoreCSISnapshotNameArg, &restoreArgs.Name); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, RestoreCSISnapshotPVCNameArg, &restoreArgs.PVC); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, RestoreCSISnapshotNamespaceArg, &restoreArgs.Namespace); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, RestoreCSISnapshotStorageClassArg, &restoreArgs.StorageClass); err != nil { | ||
return nil, err | ||
} | ||
if err := Arg(args, RestoreCSISnapshotRestoreSizeArg, &restoreSize); err != nil { | ||
return nil, err | ||
} | ||
if err := OptArg(args, RestoreCSISnapshotAccessModesArg, &restoreArgs.AccessModes, []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}); err != nil { | ||
return nil, err | ||
} | ||
if err := validateVolumeAccessModesArg(restoreArgs.AccessModes); err != nil { | ||
return nil, err | ||
} | ||
if err := OptArg(args, RestoreCSISnapshotVolumeModeArg, &restoreArgs.VolumeMode, v1.PersistentVolumeFilesystem); err != nil { | ||
return nil, err | ||
} | ||
if err := validateVolumeModeArg(restoreArgs.VolumeMode); err != nil { | ||
return nil, err | ||
} | ||
if err := OptArg(args, RestoreCSISnapshotLabelsArg, &restoreArgs.Labels, nil); err != nil { | ||
return nil, err | ||
} | ||
size, err := resource.ParseQuantity(restoreSize) | ||
if err != nil { | ||
return nil, err | ||
} | ||
restoreArgs.RestoreSize = &size | ||
|
||
kubeCli, err := getClient() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if _, err := restoreCSISnapshot(ctx, kubeCli, restoreArgs); err != nil { | ||
return nil, err | ||
} | ||
return nil, nil | ||
} | ||
|
||
func (*restoreCSISnapshotFunc) RequiredArgs() []string { | ||
return []string{ | ||
RestoreCSISnapshotNameArg, | ||
RestoreCSISnapshotPVCNameArg, | ||
RestoreCSISnapshotNamespaceArg, | ||
RestoreCSISnapshotStorageClassArg, | ||
RestoreCSISnapshotRestoreSizeArg, | ||
} | ||
} | ||
|
||
func getClient() (kubernetes.Interface, error) { | ||
kubeCli, err := kube.NewClient() | ||
return kubeCli, err | ||
} | ||
|
||
func restoreCSISnapshot(ctx context.Context, kubeCli kubernetes.Interface, args restoreCSISnapshotArgs) (*v1.PersistentVolumeClaim, error) { | ||
pvc := newPVCManifest(args) | ||
if _, err := kubeCli.CoreV1().PersistentVolumeClaims(args.Namespace).Create(ctx, pvc, metav1.CreateOptions{}); err != nil { | ||
return nil, err | ||
} | ||
return pvc, nil | ||
} | ||
|
||
func newPVCManifest(args restoreCSISnapshotArgs) *v1.PersistentVolumeClaim { | ||
snapshotAPIGroup := SnapshotAPIGroup | ||
pvc := &v1.PersistentVolumeClaim{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: args.PVC, | ||
Namespace: args.Namespace, | ||
}, | ||
Spec: v1.PersistentVolumeClaimSpec{ | ||
AccessModes: args.AccessModes, | ||
VolumeMode: &args.VolumeMode, | ||
DataSource: &v1.TypedLocalObjectReference{ | ||
APIGroup: &snapshotAPIGroup, | ||
Kind: "VolumeSnapshot", | ||
Name: args.Name, | ||
}, | ||
StorageClassName: &args.StorageClass, | ||
Resources: v1.ResourceRequirements{ | ||
Requests: v1.ResourceList{ | ||
v1.ResourceStorage: *args.RestoreSize, | ||
}, | ||
}, | ||
}, | ||
} | ||
if args.Labels != nil { | ||
pvc.ObjectMeta.Labels = args.Labels | ||
} | ||
return pvc | ||
} | ||
|
||
func validateVolumeModeArg(volumeMode v1.PersistentVolumeMode) error { | ||
switch volumeMode { | ||
case v1.PersistentVolumeFilesystem, | ||
v1.PersistentVolumeBlock: | ||
default: | ||
return errors.New("Given volumeMode " + string(volumeMode) + " is invalid") | ||
} | ||
return nil | ||
} | ||
|
||
func validateVolumeAccessModesArg(accessModes []v1.PersistentVolumeAccessMode) error { | ||
for _, accessModeInArg := range accessModes { | ||
switch accessModeInArg { | ||
case v1.ReadOnlyMany, | ||
v1.ReadWriteMany, | ||
v1.ReadWriteOnce: | ||
default: | ||
return errors.New("Given accessMode " + string(accessModeInArg) + " is invalid") | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.