diff --git a/go.mod b/go.mod
index 4ff331d6c6..78e88ca309 100644
--- a/go.mod
+++ b/go.mod
@@ -43,7 +43,6 @@ require (
github.com/prometheus/client_model v0.6.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
- github.com/vmware/govmomi v0.36.1
go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.18.0
gonum.org/v1/gonum v0.14.0
diff --git a/go.sum b/go.sum
index 9159b67b63..cf03ebe4f1 100644
--- a/go.sum
+++ b/go.sum
@@ -146,8 +146,6 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dougm/pretty v0.0.0-20171025230240-2ee9d7453c02 h1:tR3jsKPiO/mb6ntzk/dJlHZtm37CPfVp1C9KIo534+4=
-github.com/dougm/pretty v0.0.0-20171025230240-2ee9d7453c02/go.mod h1:7NQ3kWOx2cZOSjtcveTa5nqupVr2s6/83sG+rTlI7uA=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
@@ -526,8 +524,6 @@ github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/tg123/go-htpasswd v1.2.2 h1:tmNccDsQ+wYsoRfiONzIhDm5OkVHQzN3w4FOBAlN6BY=
github.com/tg123/go-htpasswd v1.2.2/go.mod h1:FcIrK0J+6zptgVwK1JDlqyajW/1B4PtuJ/FLWl7nx8A=
-github.com/vmware/govmomi v0.36.1 h1:+E/nlfteQ8JvC0xhuKAfpnMsuIeGeGj7rJwqENUcWm8=
-github.com/vmware/govmomi v0.36.1/go.mod h1:mtGWtM+YhTADHlCgJBiskSRPOZRsN9MSjPzaZLte/oQ=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
diff --git a/pkg/blockstorage/awsefs/awsefs.go b/pkg/blockstorage/awsefs/awsefs.go
deleted file mode 100644
index fdfc2d49ba..0000000000
--- a/pkg/blockstorage/awsefs/awsefs.go
+++ /dev/null
@@ -1,685 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- "context"
- "fmt"
- "strings"
-
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/session"
- "github.com/aws/aws-sdk-go/service/backup"
- awsefs "github.com/aws/aws-sdk-go/service/efs"
- "github.com/aws/aws-sdk-go/service/sts"
- uuid "github.com/gofrs/uuid"
- "github.com/pkg/errors"
- "k8s.io/apimachinery/pkg/util/rand"
-
- awsconfig "github.com/kanisterio/kanister/pkg/aws"
- "github.com/kanisterio/kanister/pkg/blockstorage"
- kantags "github.com/kanisterio/kanister/pkg/blockstorage/tags"
- "github.com/kanisterio/kanister/pkg/field"
- "github.com/kanisterio/kanister/pkg/log"
-)
-
-type Efs struct {
- *awsefs.EFS
- *backup.Backup
- accountID string
- region string
- role string
- backupVaultName string
-}
-
-var _ blockstorage.Provider = (*Efs)(nil)
-
-const (
- generalPurposePerformanceMode = awsefs.PerformanceModeGeneralPurpose
- defaultPerformanceMode = generalPurposePerformanceMode
-
- burstingThroughputMode = awsefs.ThroughputModeBursting
- defaultThroughputMode = burstingThroughputMode
-
- efsType = "EFS"
- maxRetries = 10
-)
-
-var allowedMetadataKeys = map[string]bool{
- "file-system-id": true,
- "Encrypted": true,
- "KmsKeyId": true,
- "PerformanceMode": true,
- "CreationToken": true,
- "newFileSystem": true,
-}
-
-// NewEFSProvider returns a blockstorage provider for AWS EFS.
-func NewEFSProvider(ctx context.Context, config map[string]string) (blockstorage.Provider, error) {
- awsConfig, region, err := awsconfig.GetConfig(ctx, config)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get configuration for EFS")
- }
- s, err := session.NewSession(awsConfig)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create session for EFS")
- }
- stsCli := sts.New(s, aws.NewConfig().WithRegion(region).WithMaxRetries(maxRetries))
- user, err := stsCli.GetCallerIdentity(&sts.GetCallerIdentityInput{})
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get user")
- }
- if user.Account == nil {
- return nil, errors.New("Account ID is empty")
- }
- accountID := *user.Account
- efsCli := awsefs.New(s, aws.NewConfig().WithRegion(region).WithCredentials(awsConfig.Credentials).WithMaxRetries(maxRetries))
- backupCli := backup.New(s, aws.NewConfig().WithRegion(region).WithCredentials(awsConfig.Credentials).WithMaxRetries(maxRetries))
-
- efsVault, ok := config[awsconfig.ConfigEFSVaultName]
- if !ok || efsVault == "" {
- return nil, errors.New("EFS vault name is empty")
- }
-
- return &Efs{
- EFS: efsCli,
- Backup: backupCli,
- region: region,
- accountID: accountID,
- role: config[awsconfig.ConfigRole],
- backupVaultName: efsVault,
- }, nil
-}
-
-func (e *Efs) Type() blockstorage.Type {
- return blockstorage.TypeEFS
-}
-
-// VolumeCreate implements interface method for EFS. It sends EFS volume create request
-// to AWS EFS and waits until the file system is available. Eventually, it returns the
-// volume info that is sent back from the AWS EFS.
-func (e *Efs) VolumeCreate(ctx context.Context, volume blockstorage.Volume) (*blockstorage.Volume, error) {
- req := &awsefs.CreateFileSystemInput{}
- reqId, err := uuid.NewV4()
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create UUID")
- }
- req.SetCreationToken(reqId.String())
- req.SetPerformanceMode(defaultPerformanceMode)
- req.SetThroughputMode(defaultThroughputMode)
- req.SetTags(convertToEFSTags(blockstorage.KeyValueToMap(volume.Tags)))
-
- fd, err := e.CreateFileSystemWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create EFS instance")
- }
- if fd.FileSystemId == nil {
- return nil, errors.New("Empty filesystem ID")
- }
- if err = e.waitUntilFileSystemAvailable(ctx, *fd.FileSystemId); err != nil {
- return nil, errors.Wrap(err, "EFS instance is not available")
- }
- vol, err := e.VolumeGet(ctx, *fd.FileSystemId, volume.Az)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get recently create EFS instance")
- }
- _, mountTargets, err := filterAndGetMountTargetsFromTags(blockstorage.KeyValueToMap(volume.Tags))
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get filtered tags and mount targets")
- }
- if err = e.createMountTargets(ctx, vol.ID, mountTargets); err != nil {
- return nil, errors.Wrap(err, "Failed to create mount targets")
- }
- return vol, nil
-}
-
-func (e *Efs) VolumeCreateFromSnapshot(ctx context.Context, snapshot blockstorage.Snapshot, tags map[string]string) (*blockstorage.Volume, error) {
- reqM := &backup.GetRecoveryPointRestoreMetadataInput{}
- reqM.SetBackupVaultName(e.backupVaultName)
- reqM.SetRecoveryPointArn(snapshot.ID)
-
- respM, err := e.GetRecoveryPointRestoreMetadataWithContext(ctx, reqM)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get backup tag from recovery point directly")
- }
- rpTags := convertFromBackupTags(respM.RestoreMetadata)
- rp2Tags, err := e.getBackupTags(ctx, snapshot.ID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get backup tag from recovery point")
- }
- rpTags = kantags.Union(rpTags, rp2Tags)
- // RestorePoint tags has some tags to describe saved mount targets.
- // We need to get them and remove them from the tags
- filteredTags, mountTargets, err := filterAndGetMountTargetsFromTags(rpTags)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get filtered tags and mount targets")
- }
- // Add some metadata which are necessary for EFS restore to function properly.
- filteredTags = kantags.Union(filteredTags, efsRestoreTags())
-
- req := &backup.StartRestoreJobInput{}
- req.SetIamRoleArn(awsDefaultServiceBackupRole(e.accountID))
-
- // Start job only allows specific keys in metadata
- // https://docs.aws.amazon.com/aws-backup/latest/devguide/API_StartRestoreJob.html
- for k := range filteredTags {
- if !allowedMetadataKeys[k] {
- delete(filteredTags, k)
- }
- }
- req.SetMetadata(convertToBackupTags(filteredTags))
- req.SetRecoveryPointArn(snapshot.ID)
- req.SetResourceType(efsType)
-
- resp, err := e.StartRestoreJobWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to start the restore job")
- }
- if resp.RestoreJobId == nil {
- return nil, errors.New("Empty restore job ID")
- }
- restoreID := *resp.RestoreJobId
- if err = e.waitUntilRestoreComplete(ctx, restoreID); err != nil {
- return nil, errors.Wrap(err, "Restore job failed to complete")
- }
- respD := &backup.DescribeRestoreJobInput{}
- respD.SetRestoreJobId(restoreID)
- descJob, err := e.DescribeRestoreJobWithContext(ctx, respD)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get description for the restore job")
- }
- fsID, err := efsIDFromResourceARN(*descJob.CreatedResourceArn)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get filesystem ID")
- }
- if err = e.createMountTargets(ctx, fsID, mountTargets); err != nil {
- return nil, errors.Wrap(err, "Failed to create mount targets")
- }
- return e.VolumeGet(ctx, fsID, "")
-}
-
-func efsRestoreTags() map[string]string {
- return map[string]string{
- "newFileSystem": "true",
- "CreationToken": rand.String(16),
- "Encrypted": "false",
- "PerformanceMode": generalPurposePerformanceMode,
- }
-}
-
-type mountTarget struct {
- subnetID string
- securityGroups []string
-}
-
-type mountTargets map[string]*mountTarget
-
-func (e *Efs) createMountTargets(ctx context.Context, fsID string, mts mountTargets) error {
- created := make([]*awsefs.MountTargetDescription, 0)
- for _, v := range mts {
- req := &awsefs.CreateMountTargetInput{}
- req.SetFileSystemId(fsID)
- req.SetSubnetId(v.subnetID)
- req.SetSecurityGroups(convertListOfStrings(v.securityGroups))
-
- mtd, err := e.CreateMountTargetWithContext(ctx, req)
- if err != nil {
- return errors.Wrap(err, "Failed to create mount target")
- }
- created = append(created, mtd)
- }
-
- for _, desc := range created {
- if err := e.waitUntilMountTargetReady(ctx, *desc.MountTargetId); err != nil {
- return errors.Wrap(err, "Failed while waiting for Mount target to be ready")
- }
- }
- return nil
-}
-
-func parseMountTargetKey(key string) (string, error) {
- if !strings.HasPrefix(key, mountTargetKeyPrefix) {
- return "", errors.New("Malformed string for mount target key")
- }
- return key[len(mountTargetKeyPrefix):], nil
-}
-
-func parseMountTargetValue(value string) (*mountTarget, error) {
- // Format:
- // String until the first "+" is subnetID
- // After that "+" separates security groups
- // Example value:
- // subnet-123+securityGroup-1+securityGroup-2
- tokens := strings.Split(value, securityGroupSeparator)
- if len(tokens) < 1 {
- return nil, errors.New("Malformed string for mount target values")
- }
- subnetID := tokens[0]
- sgs := make([]string, 0)
- if len(tokens) > 1 {
- sgs = append(sgs, tokens[1:]...)
- }
- return &mountTarget{
- subnetID: subnetID,
- securityGroups: sgs,
- }, nil
-}
-
-func filterAndGetMountTargetsFromTags(tags map[string]string) (map[string]string, mountTargets, error) {
- filteredTags := make(map[string]string)
- mts := make(mountTargets)
- for k, v := range tags {
- if strings.HasPrefix(k, mountTargetKeyPrefix) {
- id, err := parseMountTargetKey(k)
- if err != nil {
- return nil, nil, err
- }
- mt, err := parseMountTargetValue(v)
- if err != nil {
- return nil, nil, err
- }
- mts[id] = mt
- } else {
- // It is not a mount target tag, so pass it
- filteredTags[k] = v
- }
- }
- return filteredTags, mts, nil
-}
-
-func (e *Efs) getBackupTags(ctx context.Context, arn string) (map[string]string, error) {
- result := make(map[string]string)
- for resp, req := emptyResponseRequestForListTags(); resp.NextToken != nil; req.NextToken = resp.NextToken {
- var err error
- req.SetResourceArn(arn)
- resp, err = e.ListTagsWithContext(ctx, req)
- if err != nil {
- return nil, err
- }
- tags := convertFromBackupTags(resp.Tags)
- result = kantags.Union(result, tags)
- }
- return result, nil
-}
-
-func (e *Efs) VolumeDelete(ctx context.Context, volume *blockstorage.Volume) error {
- mts, err := e.getMountTargets(ctx, volume.ID)
- if isVolumeNotFound(err) {
- return nil
- }
- err = e.deleteMountTargets(ctx, mts)
- if err != nil {
- return errors.Wrap(err, "Failed to delete mount targets")
- }
-
- req := &awsefs.DeleteFileSystemInput{}
- req.SetFileSystemId(volume.ID)
- output, err := e.DeleteFileSystemWithContext(ctx, req)
- if err == nil {
- log.Info().Print("Delete EFS output", field.M{"output": output.String()})
- }
- if isVolumeNotFound(err) {
- return nil
- }
- return err
-}
-
-func (e *Efs) getMountTargets(ctx context.Context, fsID string) ([]*awsefs.MountTargetDescription, error) {
- mts := make([]*awsefs.MountTargetDescription, 0)
- for resp, req := emptyResponseRequestForMountTargets(); resp.NextMarker != nil; req.Marker = resp.NextMarker {
- var err error
- req.SetFileSystemId(fsID)
- resp, err = e.DescribeMountTargetsWithContext(ctx, req)
- if err != nil {
- return nil, err
- }
- mts = append(mts, resp.MountTargets...)
- }
- return mts, nil
-}
-
-func (e *Efs) deleteMountTargets(ctx context.Context, mts []*awsefs.MountTargetDescription) error {
- for _, mt := range mts {
- req := &awsefs.DeleteMountTargetInput{}
- req.SetMountTargetId(*mt.MountTargetId)
- _, err := e.DeleteMountTargetWithContext(ctx, req)
- if err != nil {
- return err
- }
- err = e.waitUntilMountTargetIsDeleted(ctx, *mt.MountTargetId)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (e *Efs) VolumeGet(ctx context.Context, id string, zone string) (*blockstorage.Volume, error) {
- desc, err := e.getFileSystemDescriptionWithID(ctx, id)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get EFS volume")
- }
- return volumeFromEFSDescription(desc, zone), nil
-}
-
-func (e *Efs) SnapshotCopy(ctx context.Context, from blockstorage.Snapshot, to blockstorage.Snapshot) (*blockstorage.Snapshot, error) {
- return nil, errors.New("Not implemented")
-}
-
-func (e *Efs) SnapshotCopyWithArgs(ctx context.Context, from blockstorage.Snapshot, to blockstorage.Snapshot, args map[string]string) (*blockstorage.Snapshot, error) {
- return nil, errors.New("Copy Snapshot with Args not implemented")
-}
-
-func (e *Efs) SnapshotCreate(ctx context.Context, volume blockstorage.Volume, tags map[string]string) (*blockstorage.Snapshot, error) {
- err := e.CreateBackupVaultWrapper()
- if err != nil {
- return nil, errors.Wrap(err, "Failed to setup K10 vault for AWS Backup")
- }
- desc, err := e.getFileSystemDescriptionWithID(ctx, volume.ID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get corresponding description")
- }
-
- req := &backup.StartBackupJobInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetIamRoleArn(awsDefaultServiceBackupRole(e.accountID))
- req.SetResourceArn(resourceARNForEFS(e.region, *desc.OwnerId, *desc.FileSystemId))
-
- // Save mount points and security groups as tags
- infraTags, err := e.getMountPointAndSecurityGroupTags(ctx, volume.ID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get mount points and security groups")
- }
- allTags := kantags.Union(tags, infraTags)
- req.SetRecoveryPointTags(convertToBackupTags(allTags))
- resp, err := e.StartBackupJob(req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to start a backup job")
- }
- if err = e.waitUntilRecoveryPointVisible(ctx, *resp.RecoveryPointArn); err != nil {
- return nil, errors.Wrap(err, "Failed to fetch recovery point")
- }
- if err = e.setBackupTags(ctx, *resp.RecoveryPointArn, infraTags); err != nil {
- return nil, errors.Wrap(err, "Failed to set backup tags")
- }
-
- req2 := &backup.DescribeRecoveryPointInput{}
- req2.SetBackupVaultName(e.backupVaultName)
- req2.SetRecoveryPointArn(*resp.RecoveryPointArn)
- describeRP, err := e.DescribeRecoveryPointWithContext(ctx, req2)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get recovery point information")
- }
- return &blockstorage.Snapshot{
- CreationTime: blockstorage.TimeStamp(*resp.CreationDate),
- Encrypted: volume.Encrypted,
- ID: *resp.RecoveryPointArn,
- Region: e.region,
- SizeInBytes: *describeRP.BackupSizeInBytes,
- Tags: blockstorage.MapToKeyValue(allTags),
- Volume: &volume,
- Type: blockstorage.TypeEFS,
- }, nil
-}
-
-// Create a Backup Vault, also checks if vault already exist
-func (e *Efs) CreateBackupVaultWrapper() error {
- req := &backup.CreateBackupVaultInput{}
- req.SetBackupVaultName(e.backupVaultName)
-
- _, err := e.CreateBackupVault(req)
- if isBackupVaultAlreadyExists(err) {
- return nil
- }
- return err
-}
-
-func (e *Efs) SnapshotCreateWaitForCompletion(ctx context.Context, snapshot *blockstorage.Snapshot) error {
- return e.waitUntilRecoveryPointCompleted(ctx, snapshot.ID)
-}
-
-func (e *Efs) SnapshotDelete(ctx context.Context, snapshot *blockstorage.Snapshot) error {
- req := &backup.DeleteRecoveryPointInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetRecoveryPointArn(snapshot.ID)
-
- output, err := e.DeleteRecoveryPointWithContext(ctx, req)
- if err == nil {
- log.Info().Print("Delete EFS snapshot", field.M{"output": output.String()})
- }
- if isResourceNotFoundException(err) {
- return nil
- }
- if isDeleteInProgress(err) {
- return nil
- }
- return err
-}
-
-func (e *Efs) SnapshotGet(ctx context.Context, id string) (*blockstorage.Snapshot, error) {
- req := &backup.DescribeRecoveryPointInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetRecoveryPointArn(id)
-
- resp, err := e.DescribeRecoveryPointWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get recovery point information")
- }
- if resp.ResourceArn == nil {
- return nil, errors.Wrap(err, "Resource ARN in recovery point is empty")
- }
- volID, err := efsIDFromResourceARN(*resp.ResourceArn)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get volume ID from recovery point ARN")
- }
- vol, err := e.VolumeGet(ctx, volID, "")
- if err != nil && !isVolumeNotFound(err) {
- return nil, errors.Wrap(err, "Failed to get filesystem")
- }
- return snapshotFromRecoveryPoint(resp, vol, e.region)
-}
-
-func (e *Efs) SetTags(ctx context.Context, resource interface{}, tags map[string]string) error {
- switch r := resource.(type) {
- case *blockstorage.Volume:
- return e.setEFSTags(ctx, r.ID, tags)
- case *blockstorage.Snapshot:
- return e.setBackupTags(ctx, r.ID, tags)
- default:
- return errors.New("Unsupported type for setting tags")
- }
-}
-
-func (e *Efs) setBackupTags(ctx context.Context, arn string, tags map[string]string) error {
- if len(tags) == 0 {
- return nil
- }
- req := &backup.TagResourceInput{
- ResourceArn: &arn,
- Tags: convertToBackupTags(tags),
- }
- _, err := e.Backup.TagResourceWithContext(ctx, req)
- return err
-}
-
-func (e *Efs) setEFSTags(ctx context.Context, id string, tags map[string]string) error {
- if len(tags) == 0 {
- return nil
- }
- req := &awsefs.TagResourceInput{
- ResourceId: &id,
- Tags: convertToEFSTags(tags),
- }
- _, err := e.EFS.TagResource(req)
- return err
-}
-
-func (e *Efs) VolumesList(ctx context.Context, tags map[string]string, zone string) ([]*blockstorage.Volume, error) {
- result := make([]*blockstorage.Volume, 0)
- for resp, req := emptyResponseRequestForFilesystems(); resp.NextMarker != nil; req.Marker = resp.NextMarker {
- var err error
- resp, err = e.DescribeFileSystemsWithContext(ctx, req)
- if err != nil {
- return nil, err
- }
- availables := filterAvailable(filterWithTags(resp.FileSystems, tags))
- result = append(result, volumesFromEFSDescriptions(availables, zone)...)
- }
- return result, nil
-}
-
-func (e *Efs) SnapshotsList(ctx context.Context, tags map[string]string) ([]*blockstorage.Snapshot, error) {
- result := make([]*blockstorage.Snapshot, 0)
- for resp, req := emptyResponseRequestForBackups(); resp.NextToken != nil; req.NextToken = resp.NextToken {
- var err error
- req.SetBackupVaultName(e.backupVaultName)
- resp, err = e.ListRecoveryPointsByBackupVaultWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to list recovery points by vault")
- }
- snaps, err := e.SnapshotsFromRecoveryPoints(ctx, resp.RecoveryPoints)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshots from recovery points")
- }
- result = append(result, blockstorage.FilterSnapshotsWithTags(snaps, tags)...)
- }
- return result, nil
-}
-
-// List a limited amount of snapshots based on given limit input
-func (e *Efs) SnapshotsListWLimit(ctx context.Context, tags map[string]string, limit int64) ([]*blockstorage.Snapshot, error) {
- result := make([]*blockstorage.Snapshot, 0)
- var err error
- req := &backup.ListRecoveryPointsByBackupVaultInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetMaxResults(limit)
- resp, err := e.ListRecoveryPointsByBackupVaultWithContext(ctx, req) // backup API
- if err != nil {
- return nil, errors.Wrap(err, "Failed to list recovery points by vault")
- }
- snaps, err := e.SnapshotsFromRecoveryPoints(ctx, resp.RecoveryPoints)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshots from recovery points")
- }
- result = append(result, blockstorage.FilterSnapshotsWithTags(snaps, tags)...)
- return result, err
-}
-
-func (e *Efs) SnapshotsFromRecoveryPoints(ctx context.Context, rps []*backup.RecoveryPointByBackupVault) ([]*blockstorage.Snapshot, error) {
- result := make([]*blockstorage.Snapshot, 0)
- for _, rp := range rps {
- if rp.RecoveryPointArn == nil {
- return nil, errors.New("Empty ARN in recovery point")
- }
- tags, err := e.getBackupTags(ctx, *rp.RecoveryPointArn)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get backup tags")
- }
- volID, err := efsIDFromResourceARN(*rp.ResourceArn)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get volume ID from recovery point ARN")
- }
- // VolumeGet might return error since originating filesystem might have
- // been deleted.
- vol, err := e.VolumeGet(ctx, volID, "")
- if err != nil && !isVolumeNotFound(err) {
- return nil, errors.Wrap(err, "Failed to get filesystem")
- }
- snap, err := snapshotFromRecoveryPointByVault(rp, vol, tags, e.region)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshot from the vault")
- }
- result = append(result, snap)
- }
- return result, nil
-}
-
-func emptyResponseRequestForBackups() (*backup.ListRecoveryPointsByBackupVaultOutput, *backup.ListRecoveryPointsByBackupVaultInput) {
- resp := (&backup.ListRecoveryPointsByBackupVaultOutput{}).SetNextToken("")
- req := &backup.ListRecoveryPointsByBackupVaultInput{}
- return resp, req
-}
-
-func emptyResponseRequestForFilesystems() (*awsefs.DescribeFileSystemsOutput, *awsefs.DescribeFileSystemsInput) {
- resp := (&awsefs.DescribeFileSystemsOutput{}).SetNextMarker("")
- req := &awsefs.DescribeFileSystemsInput{}
- return resp, req
-}
-
-func emptyResponseRequestForListTags() (*backup.ListTagsOutput, *backup.ListTagsInput) {
- resp := (&backup.ListTagsOutput{}).SetNextToken("")
- req := &backup.ListTagsInput{}
- return resp, req
-}
-
-func emptyResponseRequestForMountTargets() (*awsefs.DescribeMountTargetsOutput, *awsefs.DescribeMountTargetsInput) {
- resp := (&awsefs.DescribeMountTargetsOutput{}).SetNextMarker("")
- req := &awsefs.DescribeMountTargetsInput{}
- return resp, req
-}
-
-func awsDefaultServiceBackupRole(accountID string) string {
- return fmt.Sprintf("arn:aws:iam::%s:role/service-role/AWSBackupDefaultServiceRole", accountID)
-}
-
-func resourceARNForEFS(region string, accountID string, fileSystemID string) string {
- return fmt.Sprintf("arn:aws:elasticfilesystem:%s:%s:file-system/%s", region, accountID, fileSystemID)
-}
-
-func (e *Efs) getFileSystemDescriptionWithID(ctx context.Context, id string) (*awsefs.FileSystemDescription, error) {
- req := &awsefs.DescribeFileSystemsInput{}
- req.SetFileSystemId(id)
-
- descs, err := e.DescribeFileSystemsWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get filesystem description")
- }
- availables := filterAvailable(descs.FileSystems)
- switch len(availables) {
- case 0:
- return nil, errors.New("Failed to find volume")
- case 1:
- return descs.FileSystems[0], nil
- default:
- return nil, errors.New("Unexpected condition, multiple filesystems with same ID")
- }
-}
-
-func (e *Efs) getMountPointAndSecurityGroupTags(ctx context.Context, id string) (map[string]string, error) {
- mts, err := e.getMountTargets(ctx, id)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get mount target for the volume")
- }
- resultTags := make(map[string]string)
- for _, mt := range mts {
- req := &awsefs.DescribeMountTargetSecurityGroupsInput{}
- req.SetMountTargetId(*mt.MountTargetId)
-
- resp, err := e.DescribeMountTargetSecurityGroupsWithContext(ctx, req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get security group")
- }
- if mt.SubnetId == nil {
- return nil, errors.New("Empty subnet ID in mount target entry")
- }
- value := mountTargetValue(*mt.SubnetId, resp.SecurityGroups)
- if mt.MountTargetId == nil {
- return nil, errors.New("Empty ID in mount target entry")
- }
- key := mountTargetKey(*mt.MountTargetId)
- resultTags[key] = value
- }
- return resultTags, nil
-}
diff --git a/pkg/blockstorage/awsefs/awsefs_test.go b/pkg/blockstorage/awsefs/awsefs_test.go
deleted file mode 100644
index e150f81b02..0000000000
--- a/pkg/blockstorage/awsefs/awsefs_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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 awsefs
-
-import (
- "context"
- "os"
-
- "gopkg.in/check.v1"
-
- awsconfig "github.com/kanisterio/kanister/pkg/aws"
- "github.com/kanisterio/kanister/pkg/testutil"
-)
-
-type AwsEfsSuite struct{}
-
-var _ = check.Suite(&AwsEfsSuite{})
-
-func (a *AwsEfsSuite) TestBackupVaultName(c *check.C) {
- testutil.GetEnvOrSkip(c, awsconfig.AccessKeyID)
- testutil.GetEnvOrSkip(c, awsconfig.SecretAccessKey)
- ctx := context.Background()
-
- // success
- config := map[string]string{
- awsconfig.AccessKeyID: os.Getenv(awsconfig.AccessKeyID),
- awsconfig.SecretAccessKey: os.Getenv(awsconfig.SecretAccessKey),
- awsconfig.ConfigEFSVaultName: "vault-name",
- awsconfig.ConfigRegion: "us-east-2",
- }
- _, err := NewEFSProvider(ctx, config)
- c.Assert(err, check.IsNil)
-
- // fail because Vault name is expected
- config[awsconfig.ConfigEFSVaultName] = ""
- _, err = NewEFSProvider(ctx, config)
- c.Assert(err, check.NotNil)
-}
diff --git a/pkg/blockstorage/awsefs/conversion.go b/pkg/blockstorage/awsefs/conversion.go
deleted file mode 100644
index b3eca5b3fd..0000000000
--- a/pkg/blockstorage/awsefs/conversion.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- "strings"
-
- "github.com/aws/aws-sdk-go/aws"
- awsarn "github.com/aws/aws-sdk-go/aws/arn"
- "github.com/aws/aws-sdk-go/service/backup"
- awsefs "github.com/aws/aws-sdk-go/service/efs"
- "github.com/pkg/errors"
-
- "github.com/kanisterio/kanister/pkg/blockstorage"
-)
-
-const (
- securityGroupSeparator = "+"
- mountTargetKeyPrefix = "kasten.io/aws-mount-target/"
-)
-
-// convertFromEFSTags converts AWS EFS tag structure to a flattened map.
-func convertFromEFSTags(efsTags []*awsefs.Tag) map[string]string {
- tags := make(map[string]string)
- for _, t := range efsTags {
- tags[*t.Key] = *t.Value
- }
- return tags
-}
-
-// efsIDFromResourceARN gets EFS filesystem ID from an EFS resource ARN.
-func efsIDFromResourceARN(arn string) (string, error) {
- resourceARN, err := awsarn.Parse(arn)
- if err != nil {
- return "", errors.Wrap(err, "Failed to parse ARN")
- }
- // An example of resourceArn.Resource:
- // "file-system/fs-87b1302c"
- tokens := strings.Split(resourceARN.Resource, "/")
- if len(tokens) != 2 {
- return "", errors.New("Bad resource in ARN")
- }
- if tokens[0] != "file-system" {
- return "", errors.New("Bad resource type in ARN")
- }
- return tokens[1], nil
-}
-
-func snapshotFromRecoveryPoint(rp *backup.DescribeRecoveryPointOutput, volume *blockstorage.Volume, region string) (*blockstorage.Snapshot, error) {
- if rp == nil {
- return nil, errors.New("Empty recovery point")
- }
- if rp.CreationDate == nil {
- return nil, errors.New("Recovery point has no CreationDate")
- }
- if rp.BackupSizeInBytes == nil {
- return nil, errors.New("Recovery point has no BackupSizeInBytes")
- }
- if rp.RecoveryPointArn == nil {
- return nil, errors.New("Recovery point has no RecoveryPointArn")
- }
- encrypted := false
- if volume != nil {
- encrypted = volume.Encrypted
- }
- return &blockstorage.Snapshot{
- ID: *rp.RecoveryPointArn,
- CreationTime: blockstorage.TimeStamp(*rp.CreationDate),
- SizeInBytes: *rp.BackupSizeInBytes,
- Region: region,
- Type: blockstorage.TypeEFS,
- Volume: volume,
- Encrypted: encrypted,
- Tags: nil,
- }, nil
-}
-
-func snapshotFromRecoveryPointByVault(rp *backup.RecoveryPointByBackupVault, volume *blockstorage.Volume, tags map[string]string, region string) (*blockstorage.Snapshot, error) {
- if rp == nil {
- return nil, errors.New("Empty recovery point")
- }
- if rp.CreationDate == nil {
- return nil, errors.New("Recovery point has not CreationDate")
- }
- if rp.BackupSizeInBytes == nil {
- return nil, errors.New("Recovery point has no BackupSizeInBytes")
- }
- if rp.RecoveryPointArn == nil {
- return nil, errors.New("Recovery point has no RecoveryPointArn")
- }
- encrypted := false
- if volume != nil {
- encrypted = volume.Encrypted
- }
- return &blockstorage.Snapshot{
- ID: *rp.RecoveryPointArn,
- CreationTime: blockstorage.TimeStamp(*rp.CreationDate),
- SizeInBytes: *rp.BackupSizeInBytes,
- Region: region,
- Type: blockstorage.TypeEFS,
- Volume: volume,
- Encrypted: encrypted,
- Tags: blockstorage.MapToKeyValue(tags),
- }, nil
-}
-
-// convertFromBackupTags converts an AWS Backup compliant tag structure to a flattenned map.
-func convertFromBackupTags(tags map[string]*string) map[string]string {
- result := make(map[string]string)
- for k, v := range tags {
- result[k] = *v
- }
- return result
-}
-
-// convertToBackupTags converts a flattened map to AWS Backup compliant tag structure.
-func convertToBackupTags(tags map[string]string) map[string]*string {
- backupTags := make(map[string]*string)
- for k, v := range tags {
- vPtr := new(string)
- *vPtr = v
- backupTags[k] = vPtr
- }
- return backupTags
-}
-
-// convertToEFSTags converts a flattened map to AWS EFS tag structure.
-func convertToEFSTags(tags map[string]string) []*awsefs.Tag {
- efsTags := make([]*awsefs.Tag, 0, len(tags))
- for k, v := range tags {
- efsTags = append(efsTags, &awsefs.Tag{Key: aws.String(k), Value: aws.String(v)})
- }
- return efsTags
-}
-
-// convertListOfStrings converts a flattend list to a list where each
-// element is a pointer to original elements.
-func convertListOfStrings(strs []string) []*string {
- result := make([]*string, 0)
- for i := range strs {
- result = append(result, &strs[i])
- }
- return result
-}
-
-// volumeFromEFSDescription converts an AWS EFS filesystem description to Kanister blockstorage Volume type
-// using the information in the description.
-//
-// ID of the volume is equal to EFS filesystems ID (e.g fs-bdf36586).
-// Iops is always set to 0.
-// VolumeType and Atrributes set to corresponding empty values.
-func volumeFromEFSDescription(description *awsefs.FileSystemDescription, zone string) *blockstorage.Volume {
- return &blockstorage.Volume{
- Az: zone,
- ID: *description.FileSystemId,
- Type: blockstorage.TypeEFS,
- Encrypted: *description.Encrypted,
- CreationTime: blockstorage.TimeStamp(*description.CreationTime),
- SizeInBytes: *description.SizeInBytes.Value,
- Tags: blockstorage.MapToKeyValue(convertFromEFSTags(description.Tags)),
- Iops: 0,
- VolumeType: "",
- Attributes: nil,
- }
-}
-
-// volumesFromEFSDescriptions returns the list of volumes from the EFS filesystem descriptions.
-func volumesFromEFSDescriptions(descriptions []*awsefs.FileSystemDescription, zone string) []*blockstorage.Volume {
- volumes := make([]*blockstorage.Volume, 0, len(descriptions))
- for _, desc := range descriptions {
- volumes = append(volumes, volumeFromEFSDescription(desc, zone))
- }
- return volumes
-}
-
-func mergeSecurityGroups(securityGroups []*string) string {
- dereferenced := make([]string, 0, len(securityGroups))
- for _, d := range securityGroups {
- dereferenced = append(dereferenced, *d)
- }
- return strings.Join(dereferenced, securityGroupSeparator)
-}
-
-func mountTargetKey(mountTargetID string) string {
- return mountTargetKeyPrefix + mountTargetID
-}
-
-func mountTargetValue(subnetID string, securityGroups []*string) string {
- return subnetID + securityGroupSeparator + mergeSecurityGroups(securityGroups)
-}
diff --git a/pkg/blockstorage/awsefs/conversion_test.go b/pkg/blockstorage/awsefs/conversion_test.go
deleted file mode 100644
index f959529e3f..0000000000
--- a/pkg/blockstorage/awsefs/conversion_test.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- "testing"
- "time"
-
- "github.com/aws/aws-sdk-go/aws"
- awsefs "github.com/aws/aws-sdk-go/service/efs"
- "github.com/kanisterio/kanister/pkg/blockstorage"
- . "gopkg.in/check.v1"
-)
-
-func Test(t *testing.T) { TestingT(t) }
-
-type AWSEFSConversionTestSuite struct{}
-
-var _ = Suite(&AWSEFSConversionTestSuite{})
-
-func (s *AWSEFSConversionTestSuite) TestVolumeConversion(c *C) {
- az := "us-west-2a"
- fsID := "fs-123456"
- date := time.Date(2018, 10, 1, 1, 1, 1, 1, time.UTC)
-
- tcs := []struct {
- input *awsefs.FileSystemDescription
- expected *blockstorage.Volume
- }{
- {
- input: &awsefs.FileSystemDescription{
- FileSystemId: aws.String(fsID),
- CreationTime: aws.Time(date),
- SizeInBytes: &awsefs.FileSystemSize{Value: aws.Int64(1024)},
- Encrypted: aws.Bool(true),
- Tags: []*awsefs.Tag{},
- },
- expected: &blockstorage.Volume{
- ID: fsID,
- Az: az,
- CreationTime: blockstorage.TimeStamp(date),
- SizeInBytes: 1024,
- Type: blockstorage.TypeEFS,
- Encrypted: true,
- Tags: blockstorage.VolumeTags{},
- },
- },
- {
- input: &awsefs.FileSystemDescription{
- FileSystemId: aws.String(fsID),
- CreationTime: aws.Time(date),
- SizeInBytes: &awsefs.FileSystemSize{Value: aws.Int64(2 * blockstorage.BytesInGi)},
- Encrypted: aws.Bool(false),
- Tags: []*awsefs.Tag{
- {Key: aws.String("key1"), Value: aws.String("value1")},
- {Key: aws.String("key2"), Value: aws.String("value2")},
- },
- },
- expected: &blockstorage.Volume{
- ID: fsID,
- Az: az,
- CreationTime: blockstorage.TimeStamp(date),
- SizeInBytes: 2 * blockstorage.BytesInGi,
- Type: blockstorage.TypeEFS,
- Encrypted: false,
- Tags: blockstorage.VolumeTags(
- []*blockstorage.KeyValue{
- {Key: "key1", Value: "value1"},
- {Key: "key2", Value: "value2"},
- },
- ),
- },
- },
- }
-
- for _, tc := range tcs {
- vol := volumeFromEFSDescription(tc.input, az)
- c.Check(vol.Az, Equals, tc.expected.Az)
- c.Check(vol.ID, Equals, tc.expected.ID)
- c.Check(vol.CreationTime, Equals, tc.expected.CreationTime)
- c.Check(vol.SizeInBytes, Equals, tc.expected.SizeInBytes)
- c.Check(vol.Type, Equals, tc.expected.Type)
- c.Check(vol.Encrypted, Equals, tc.expected.Encrypted)
- c.Check(vol.Tags, HasLen, len(tc.expected.Tags))
- }
-}
diff --git a/pkg/blockstorage/awsefs/error.go b/pkg/blockstorage/awsefs/error.go
deleted file mode 100644
index 523137de5b..0000000000
--- a/pkg/blockstorage/awsefs/error.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- "strings"
-
- "github.com/aws/aws-sdk-go/aws/awserr"
- "github.com/aws/aws-sdk-go/service/backup"
- awsefs "github.com/aws/aws-sdk-go/service/efs"
- "github.com/pkg/errors"
-)
-
-func isVolumeNotFound(err error) bool {
- switch errV := errors.Cause(err).(type) {
- case awserr.Error:
- return errV.Code() == awsefs.ErrCodeFileSystemNotFound
- default:
- return false
- }
-}
-
-func isBackupVaultAlreadyExists(err error) bool {
- if awsErr, ok := err.(awserr.Error); ok {
- return awsErr.Code() == backup.ErrCodeAlreadyExistsException
- }
- return false
-}
-
-func isResourceNotFoundException(err error) bool {
- if awsErr, ok := err.(awserr.Error); ok {
- return awsErr.Code() == backup.ErrCodeResourceNotFoundException
- }
- return false
-}
-
-func isMountTargetNotFound(err error) bool {
- if awsErr, ok := err.(awserr.Error); ok {
- return awsErr.Code() == awsefs.ErrCodeMountTargetNotFound
- }
- return false
-}
-
-func isDeleteInProgress(err error) bool {
- if awsErr, ok := err.(awserr.Error); ok {
- if awsErr.Code() == backup.ErrCodeInvalidRequestException &&
- strings.Contains(awsErr.Message(), "Recovery point already started the deletion process") {
- return true
- }
- }
- return false
-}
diff --git a/pkg/blockstorage/awsefs/filter.go b/pkg/blockstorage/awsefs/filter.go
deleted file mode 100644
index 83a330d7dc..0000000000
--- a/pkg/blockstorage/awsefs/filter.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- awsefs "github.com/aws/aws-sdk-go/service/efs"
-
- kantags "github.com/kanisterio/kanister/pkg/blockstorage/tags"
-)
-
-func filterAvailable(descriptions []*awsefs.FileSystemDescription) []*awsefs.FileSystemDescription {
- result := make([]*awsefs.FileSystemDescription, 0)
- for _, desc := range descriptions {
- if *desc.LifeCycleState == awsefs.LifeCycleStateAvailable {
- result = append(result, desc)
- }
- }
- return result
-}
-
-func filterWithTags(descriptions []*awsefs.FileSystemDescription, tags map[string]string) []*awsefs.FileSystemDescription {
- result := make([]*awsefs.FileSystemDescription, 0)
- for i, desc := range descriptions {
- if kantags.IsSubset(convertFromEFSTags(desc.Tags), tags) {
- result = append(result, descriptions[i])
- }
- }
- return result
-}
diff --git a/pkg/blockstorage/awsefs/wait.go b/pkg/blockstorage/awsefs/wait.go
deleted file mode 100644
index 6bf210b376..0000000000
--- a/pkg/blockstorage/awsefs/wait.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2019 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 awsefs
-
-import (
- "context"
-
- "github.com/aws/aws-sdk-go/service/backup"
- awsefs "github.com/aws/aws-sdk-go/service/efs"
- "github.com/pkg/errors"
-
- "github.com/kanisterio/kanister/pkg/poll"
-)
-
-const (
- maxNumErrorRetries = 3
-)
-
-func (e *Efs) waitUntilFileSystemAvailable(ctx context.Context, id string) error {
- return poll.WaitWithRetries(ctx, maxNumErrorRetries, poll.IsAlwaysRetryable, func(ctx context.Context) (bool, error) {
- req := &awsefs.DescribeFileSystemsInput{}
- req.SetFileSystemId(id)
-
- desc, err := e.DescribeFileSystemsWithContext(ctx, req)
- if err != nil {
- return false, err
- }
- if len(desc.FileSystems) == 0 {
- return false, nil
- }
- state := desc.FileSystems[0].LifeCycleState
- if state == nil {
- return false, nil
- }
- return *state == awsefs.LifeCycleStateAvailable, nil
- })
-}
-
-func (e *Efs) waitUntilRecoveryPointCompleted(ctx context.Context, id string) error {
- return poll.WaitWithRetries(ctx, maxNumErrorRetries, poll.IsAlwaysRetryable, func(ctx context.Context) (bool, error) {
- req := &backup.DescribeRecoveryPointInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetRecoveryPointArn(id)
-
- desc, err := e.DescribeRecoveryPointWithContext(ctx, req)
- if isResourceNotFoundException(err) {
- // Recovery point doesn't appear when the backup jobs finishes.
- // Since this case is special, it will be counted as non-error.
- return false, nil
- }
- if err != nil {
- return false, err
- }
- status := desc.Status
- if status == nil {
- return false, nil
- }
- return *status == backup.RecoveryPointStatusCompleted, nil
- })
-}
-
-func (e *Efs) waitUntilRecoveryPointVisible(ctx context.Context, id string) error {
- return poll.WaitWithRetries(ctx, maxNumErrorRetries, poll.IsAlwaysRetryable, func(ctx context.Context) (bool, error) {
- req := &backup.DescribeRecoveryPointInput{}
- req.SetBackupVaultName(e.backupVaultName)
- req.SetRecoveryPointArn(id)
-
- _, err := e.DescribeRecoveryPointWithContext(ctx, req)
- if isResourceNotFoundException(err) {
- // Recovery point doesn't appear when the backup jobs finishes.
- // Since this case is special, it will be counted as non-error.
- return false, nil
- }
- if err != nil {
- return false, err
- }
- return true, nil
- })
-}
-
-func (e *Efs) waitUntilMountTargetReady(ctx context.Context, mountTargetID string) error {
- return poll.Wait(ctx, func(ctx context.Context) (bool, error) {
- req := &awsefs.DescribeMountTargetsInput{}
- req.SetMountTargetId(mountTargetID)
-
- desc, err := e.DescribeMountTargetsWithContext(ctx, req)
- if isMountTargetNotFound(err) {
- return false, nil
- }
- if err != nil {
- return false, err
- }
- if len(desc.MountTargets) != 1 {
- return false, errors.New("Returned list must have 1 entry")
- }
- mt := desc.MountTargets[0]
- state := mt.LifeCycleState
- if state == nil {
- return false, nil
- }
- return *state == awsefs.LifeCycleStateAvailable, nil
- })
-}
-
-func (e *Efs) waitUntilMountTargetIsDeleted(ctx context.Context, mountTargetID string) error {
- return poll.Wait(ctx, func(ctx context.Context) (bool, error) {
- req := &awsefs.DescribeMountTargetsInput{}
- req.SetMountTargetId(mountTargetID)
-
- _, err := e.DescribeMountTargetsWithContext(ctx, req)
- if isMountTargetNotFound(err) {
- return true, nil
- }
- if err != nil {
- return false, err
- }
- return false, nil
- })
-}
-
-func (e *Efs) waitUntilRestoreComplete(ctx context.Context, restoreJobID string) error {
- return poll.Wait(ctx, func(ctx context.Context) (bool, error) {
- req := &backup.DescribeRestoreJobInput{}
- req.SetRestoreJobId(restoreJobID)
-
- resp, err := e.DescribeRestoreJobWithContext(ctx, req)
- if err != nil {
- return false, err
- }
- if resp.Status == nil {
- return false, errors.New("Failed to get restore job status")
- }
- switch *resp.Status {
- case backup.RestoreJobStatusCompleted:
- return true, nil
- case backup.RestoreJobStatusAborted:
- return false, errors.Errorf("Restore job aborted (%s)\n", resp.String())
- case backup.RestoreJobStatusFailed:
- return false, errors.Errorf("Restore job failed (%s)\n", resp.String())
- default:
- return false, nil
- }
- })
-}
diff --git a/pkg/blockstorage/tags/tags.go b/pkg/blockstorage/tags/tags.go
index c27a2d4b29..ae99e2eec0 100644
--- a/pkg/blockstorage/tags/tags.go
+++ b/pkg/blockstorage/tags/tags.go
@@ -102,16 +102,3 @@ func IsSubset(set map[string]string, subset map[string]string) bool {
}
return true
}
-
-// Union returns union of first and second as a new map.
-// second's values have priority if a key from first and second collides.
-func Union(first map[string]string, second map[string]string) map[string]string {
- result := make(map[string]string)
- for k, v := range first {
- result[k] = v
- }
- for k, v := range second {
- result[k] = v
- }
- return result
-}
diff --git a/pkg/blockstorage/vmware/conversion.go b/pkg/blockstorage/vmware/conversion.go
deleted file mode 100644
index 1ccfb9def9..0000000000
--- a/pkg/blockstorage/vmware/conversion.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package vmware
-
-import (
- "strings"
-
- "github.com/pkg/errors"
- "github.com/vmware/govmomi/vim25/types"
-
- "github.com/kanisterio/kanister/pkg/blockstorage"
-)
-
-func convertFromObjectToVolume(vso *types.VStorageObject) (*blockstorage.Volume, error) {
- if vso == nil {
- return nil, errors.New("Empty object")
- }
- return &blockstorage.Volume{
- Type: blockstorage.TypeFCD,
- ID: vso.Config.Id.Id,
- CreationTime: blockstorage.TimeStamp(vso.Config.CreateTime),
- SizeInBytes: vso.Config.CapacityInMB * blockstorage.BytesInMi,
- Az: "",
- Iops: 0,
- Encrypted: false,
- VolumeType: "",
- Tags: blockstorage.VolumeTags{},
- Attributes: map[string]string{},
- }, nil
-}
-
-func convertFromObjectToSnapshot(vso *types.VStorageObjectSnapshotInfoVStorageObjectSnapshot, volID string) (*blockstorage.Snapshot, error) {
- if vso == nil {
- return nil, errors.New("Empty object")
- }
- return &blockstorage.Snapshot{
- Type: blockstorage.TypeFCD,
- CreationTime: blockstorage.TimeStamp(vso.CreateTime),
- ID: SnapshotFullID(volID, vso.Id.Id),
- SizeInBytes: 0,
- Region: "",
- Encrypted: false,
- }, nil
-}
-
-// vimID wraps ID string with vim25.ID struct.
-func vimID(id string) types.ID {
- return types.ID{
- Id: id,
- }
-}
-
-// SnapshotFullID create a composite identifier for a volume snapshot.
-func SnapshotFullID(volID, snapshotID string) string {
- return volID + ":" + snapshotID
-}
-
-// SplitSnapshotFullID splits a volume snapshot composite identifier into its components.
-func SplitSnapshotFullID(fullID string) (volID string, snapshotID string, err error) {
- split := strings.Split(fullID, ":")
- if len(split) != 2 {
- return "", "", errors.New("Malformed full ID for snapshot")
- }
- if len(split[0]) == 0 || len(split[1]) == 0 {
- return "", "", errors.New("Malformed volume ID or snapshot ID")
- }
- return split[0], split[1], nil
-}
-
-func convertKeyValueToTags(kvs []types.KeyValue) []*blockstorage.KeyValue {
- tags := make(map[string]string)
- for _, kv := range kvs {
- tags[kv.Key] = kv.Value
- }
- return blockstorage.MapToKeyValue(tags)
-}
-
-func convertTagsToKeyValue(tags map[string]string) []types.KeyValue {
- result := make([]types.KeyValue, 0)
- for k, v := range tags {
- var kv types.KeyValue
- kv.Key = k
- kv.Value = v
- result = append(result, kv)
- }
- return result
-}
diff --git a/pkg/blockstorage/vmware/conversion_test.go b/pkg/blockstorage/vmware/conversion_test.go
deleted file mode 100644
index 77f730f8ec..0000000000
--- a/pkg/blockstorage/vmware/conversion_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package vmware
-
-import (
- . "gopkg.in/check.v1"
-)
-
-type VMWareConversionSuite struct{}
-
-var _ = Suite(&VMWareConversionSuite{})
-
-func (s *VMWareConversionSuite) TestSnapshotIDConversion(c *C) {
- for _, tc := range []struct {
- fullID string
- errCheck Checker
- }{
- {
- fullID: "1234-abcd-5678-9213:3413-abcd-1234-1234",
- errCheck: IsNil,
- },
- {
- fullID: "1234-abcd-5678-9213:",
- errCheck: NotNil,
- },
- {
- fullID: ":3413-abcd-1234-1234",
- errCheck: NotNil,
- },
- {
- fullID: "1234-abcd-5678-9213",
- errCheck: NotNil,
- },
- } {
- volID, snapID, err := SplitSnapshotFullID(tc.fullID)
- c.Check(err, tc.errCheck)
- if tc.errCheck == IsNil {
- fullID := SnapshotFullID(volID, snapID)
- c.Check(tc.fullID, Equals, fullID)
- }
- }
-}
diff --git a/pkg/blockstorage/vmware/vmware.go b/pkg/blockstorage/vmware/vmware.go
deleted file mode 100644
index 71ac4c2f4c..0000000000
--- a/pkg/blockstorage/vmware/vmware.go
+++ /dev/null
@@ -1,870 +0,0 @@
-package vmware
-
-import (
- "context"
- stderrors "errors"
- "fmt"
- "net/url"
- "os"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "github.com/gofrs/uuid"
- "github.com/pkg/errors"
- "github.com/vmware/govmomi/cns"
- govmomitask "github.com/vmware/govmomi/task"
- "github.com/vmware/govmomi/vapi/rest"
- vapitags "github.com/vmware/govmomi/vapi/tags"
- "github.com/vmware/govmomi/vim25"
- "github.com/vmware/govmomi/vim25/methods"
- "github.com/vmware/govmomi/vim25/soap"
- "github.com/vmware/govmomi/vim25/types"
- "github.com/vmware/govmomi/vslm"
- vslmtypes "github.com/vmware/govmomi/vslm/types"
- "k8s.io/apimachinery/pkg/util/wait"
-
- "github.com/kanisterio/kanister/pkg/blockstorage"
- ktags "github.com/kanisterio/kanister/pkg/blockstorage/tags"
- "github.com/kanisterio/kanister/pkg/field"
- "github.com/kanisterio/kanister/pkg/log"
-)
-
-var _ blockstorage.Provider = (*FcdProvider)(nil)
-
-const (
- // VSphereLoginURLKey represents key in config to establish connection.
- // It should contain the username and the password.
- VSphereLoginURLKey = "VSphereLoginURL"
-
- // VSphereEndpointKey represents key for the login endpoint.
- VSphereEndpointKey = "VSphereEndpoint"
- // VSphereUsernameKey represents key for the username.
- VSphereUsernameKey = "VSphereUsername"
- // VSpherePasswordKey represents key for the password.
- VSpherePasswordKey = "VSpherePasswordKey"
-
- // VSphereIsParaVirtualizedKey is the key for the para-virtualized indicator.
- // A value of "true" or "1" implies use of a para-virtualized CSI driver in the
- // cluster. When this is true then some operations will fail.
- // Note: it is up to the creator of the provider to determine if a para-virtualized environment exists.
- VSphereIsParaVirtualizedKey = "VSphereIsParaVirtualizedKey"
-
- defaultWaitTime = 60 * time.Minute
- defaultRetryLimit = 30 * time.Minute
-
- vmWareTimeoutMinEnv = "VMWARE_GOM_TIMEOUT_MIN"
-
- // DescriptionTag is the prefix of the tags that should be placed in the snapshot description.
- // This constant must be used by clients, so changing this field may make already created snapshots inaccessible.
- DescriptionTag = "kanister.fcd.description"
- // VolumeIDListTag is the predefined name of the tag which contains volume ids separated by comma
- VolumeIDListTag = "kanister.fcd.volume-id"
-)
-
-var (
- vmWareTimeout = time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
-
- ErrNotSupportedWithParaVirtualizedVolumes = stderrors.New("operation not supported with para-virtualized volumes")
-)
-
-// FcdProvider provides blockstorage.Provider
-type FcdProvider struct {
- Gom *vslm.GlobalObjectManager
- Cns *cns.Client
- TagsSvc *vapitags.Manager
- tagManager tagManager
- categoryID string
- isParaVirtualized bool
-}
-
-// NewProvider creates new VMWare FCD provider with the config.
-// URL taken from config helps to establish connection.
-func NewProvider(config map[string]string) (blockstorage.Provider, error) {
- ep, ok := config[VSphereEndpointKey]
- if !ok {
- return nil, errors.New("Failed to find VSphere endpoint value")
- }
- username, ok := config[VSphereUsernameKey]
- if !ok {
- return nil, errors.New("Failed to find VSphere username value")
- }
- password, ok := config[VSpherePasswordKey]
- if !ok {
- return nil, errors.New("Failed to find VSphere password value")
- }
-
- u := &url.URL{Scheme: "https", Host: ep, Path: "/sdk"}
- soapCli := soap.NewClient(u, true)
- ctx := context.Background()
- cli, err := vim25.NewClient(ctx, soapCli)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create VIM client")
- }
- req := types.Login{
- This: *cli.ServiceContent.SessionManager,
- }
- req.UserName = username
- req.Password = password
- _, err = methods.Login(ctx, cli, &req)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to login")
- }
- cnsCli, err := cns.NewClient(ctx, cli)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create CNS client")
- }
- vslmCli, err := vslm.NewClient(ctx, cli)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create VSLM client")
- }
- c := rest.NewClient(cli)
- err = c.Login(ctx, url.UserPassword(username, password))
- if err != nil {
- return nil, errors.Wrap(err, "Failed to login to VAPI rest client")
- }
- tm := vapitags.NewManager(c)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create tag manager")
- }
- gom := vslm.NewGlobalObjectManager(vslmCli)
- return &FcdProvider{
- Cns: cnsCli,
- Gom: gom,
- TagsSvc: tm,
- tagManager: tm,
- isParaVirtualized: configIsParaVirtualized(config),
- }, nil
-}
-
-func configIsParaVirtualized(config map[string]string) bool {
- if isParaVirtualizedVal, ok := config[VSphereIsParaVirtualizedKey]; ok {
- if strings.ToLower(isParaVirtualizedVal) == "true" || isParaVirtualizedVal == "1" {
- return true
- }
- }
- return false
-}
-
-// IsParaVirtualized is not part of blockstorage.Provider.
-func (p *FcdProvider) IsParaVirtualized() bool {
- return p.isParaVirtualized
-}
-
-// Type is part of blockstorage.Provider
-func (p *FcdProvider) Type() blockstorage.Type {
- return blockstorage.TypeFCD
-}
-
-// Type is part of blockstorage.Provider
-func (p *FcdProvider) SetCategoryID(categoryID string) {
- p.categoryID = categoryID
-}
-
-// VolumeCreate is part of blockstorage.Provider
-func (p *FcdProvider) VolumeCreate(ctx context.Context, volume blockstorage.Volume) (*blockstorage.Volume, error) {
- return nil, errors.New("Not implemented")
-}
-
-// VolumeCreateFromSnapshot is part of blockstorage.Provider
-func (p *FcdProvider) VolumeCreateFromSnapshot(ctx context.Context, snapshot blockstorage.Snapshot, tags map[string]string) (*blockstorage.Volume, error) {
- if p.IsParaVirtualized() {
- return nil, errors.WithStack(ErrNotSupportedWithParaVirtualizedVolumes)
- }
- volID, snapshotID, err := SplitSnapshotFullID(snapshot.ID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to split snapshot full ID")
- }
- log.Debug().Print("CreateDiskFromSnapshot foo", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- uid, err := uuid.NewV1()
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create UUID")
- }
- task, err := p.Gom.CreateDiskFromSnapshot(ctx, vimID(volID), vimID(snapshotID), uid.String(), nil, nil, "")
- if err != nil {
- return nil, errors.Wrap(err, "Failed to create disk from snapshot")
- }
- log.Debug().Print("Started CreateDiskFromSnapshot task", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- res, err := task.Wait(ctx, vmWareTimeout)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to wait on task")
- }
- if res == nil {
- return nil, errors.Errorf("vSphere task did not complete. TaskRefType: %s, TaskRefValue: %s, VolID: %s, SnapshotID: %s, NewVolID: %s",
- task.ManagedObjectReference.Type, task.ManagedObjectReference.Value, volID, snapshotID, uid)
- }
- log.Debug().Print("CreateDiskFromSnapshot task complete", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- obj, ok := res.(types.VStorageObject)
- if !ok {
- return nil, errors.New(fmt.Sprintf("Wrong type returned for vSphere. Type: %T, Value: %v", res, res))
- }
- vol, err := p.VolumeGet(ctx, obj.Config.Id.Id, "")
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get volume")
- }
- tagsCNS := make(map[string]string)
- tagsCNS["cns.tag"] = "1"
- tags = ktags.Union(tags, tagsCNS)
- if err = p.SetTags(ctx, vol, tags); err != nil {
- return nil, errors.Wrap(err, "Failed to set tags")
- }
- log.Debug().Print("CreateDiskFromSnapshot complete", field.M{"SnapshotID": snapshotID, "NewVolumeID": vol.ID})
- return p.VolumeGet(ctx, vol.ID, "")
-}
-
-// VolumeDelete is part of blockstorage.Provider
-func (p *FcdProvider) VolumeDelete(ctx context.Context, volume *blockstorage.Volume) error {
- task, err := p.Gom.Delete(ctx, vimID(volume.ID))
- if err != nil {
- return errors.Wrap(err, "Failed to delete the disk")
- }
- _, err = task.Wait(ctx, vmWareTimeout)
- return err
-}
-
-// VolumeGet is part of blockstorage.Provider
-func (p *FcdProvider) VolumeGet(ctx context.Context, id string, zone string) (*blockstorage.Volume, error) {
- obj, err := p.Gom.Retrieve(ctx, vimID(id))
- if err != nil {
- return nil, errors.Wrap(err, "Failed to query the disk")
- }
- kvs, err := p.Gom.RetrieveMetadata(ctx, vimID(id), nil, "")
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get volume metadata")
- }
- vol, err := convertFromObjectToVolume(obj)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to convert object to volume")
- }
- vol.Tags = convertKeyValueToTags(kvs)
- return vol, nil
-}
-
-// SnapshotCopy is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotCopy(ctx context.Context, from blockstorage.Snapshot, to blockstorage.Snapshot) (*blockstorage.Snapshot, error) {
- return nil, errors.New("Not implemented")
-}
-
-// SnapshotCopyWithArgs is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotCopyWithArgs(ctx context.Context, from blockstorage.Snapshot, to blockstorage.Snapshot, args map[string]string) (*blockstorage.Snapshot, error) {
- return nil, errors.New("Copy Snapshot with Args not implemented")
-}
-
-var reVslmSyncFaultFatal = regexp.MustCompile("Change tracking invalid or disk in use")
-
-// SnapshotCreate is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotCreate(ctx context.Context, volume blockstorage.Volume, tags map[string]string) (*blockstorage.Snapshot, error) {
- var res types.AnyType
- description := generateSnapshotDescription(tags)
- err := wait.PollUntilContextTimeout(ctx, time.Second, defaultRetryLimit, false, func(context.Context) (bool, error) {
- timeOfCreateSnapshotCall := time.Now()
- var createErr error
- res, createErr = p.createSnapshotAndWaitForCompletion(volume, ctx, description)
- if createErr == nil {
- return true, nil
- }
-
- // it's possible that snapshot was created despite of SOAP errors,
- // we're trying to find snapshot created in the current iteration(using timeOfCreateSnapshotCall)
- // so we won't reuse snapshots created in previous runs
- foundSnapId := p.getCreatedSnapshotID(ctx, description, volume.ID, timeOfCreateSnapshotCall)
- if foundSnapId != nil {
- res = *foundSnapId
- log.Error().WithError(createErr).Print("snapshot created with errors")
- return true, nil
- }
-
- if !soap.IsVimFault(createErr) {
- return false, errors.Wrap(createErr, "Failed to wait on task")
- }
-
- // snapshot wasn't created, handle the different SOAP errors then retry
- switch t := soap.ToVimFault(createErr).(type) {
- case *types.InvalidState:
- log.Error().WithError(createErr).Print("There is some operation, other than this CreateSnapshot invocation, on the VM attached still being protected by its VM state. Will retry", field.M{"VolumeID": volume.ID})
- return false, nil
- case *vslmtypes.VslmSyncFault: // potentially can leak snapshots
- log.Error().Print(fmt.Sprintf("VslmSyncFault: %#v", t))
- if !(govmomiError{createErr}).Matches(reVslmSyncFaultFatal) {
- log.Error().Print(fmt.Sprintf("CreateSnapshot failed with VslmSyncFault. Will retry: %s", (govmomiError{createErr}).Format()), field.M{"VolumeID": volume.ID})
- return false, nil
- }
- return false, errors.Wrap(createErr, "CreateSnapshot failed with VslmSyncFault. A snapshot may have been created by this failed operation")
- case *types.NotFound:
- log.Error().WithError(createErr).Print("CreateSnapshot failed with NotFound error. Will retry", field.M{"VolumeID": volume.ID})
- return false, nil
- default:
- return false, errors.Wrap(createErr, "Failed to wait on task")
- }
- })
- if err != nil {
- log.Error().WithError(err).Print(fmt.Sprintf("Failed to create snapshot for FCD %s: %s", volume.ID, govmomiError{err}.Format()))
- return nil, errors.Wrap(err, fmt.Sprintf("Failed to create snapshot for FCD %s", volume.ID))
- }
- id, ok := res.(types.ID)
- if !ok {
- return nil, errors.New(fmt.Sprintf("Unexpected type returned for FCD %s", volume.ID))
- }
- snap, err := p.SnapshotGet(ctx, SnapshotFullID(volume.ID, id.Id))
- if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("Failed to get snapshot %s:%s", volume.ID, id.Id))
- }
- log.Debug().Print("SnapshotCreate complete", field.M{"VolumeID": volume.ID, "SnapshotID": snap.ID})
- // We don't get size information from `SnapshotGet` - so set this to the volume size for now
- if snap.SizeInBytes == 0 {
- snap.SizeInBytes = volume.SizeInBytes
- }
- snap.Volume = &volume
-
- if err = p.SetTags(ctx, snap, getTagsWithoutDescription(tags)); err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("Failed to set tags for snapshot %s:%s", volume.ID, snap.ID))
- }
-
- return snap, nil
-}
-
-// SnapshotCreateWaitForCompletion is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotCreateWaitForCompletion(ctx context.Context, snapshot *blockstorage.Snapshot) error {
- return nil
-}
-
-// SnapshotDelete is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotDelete(ctx context.Context, snapshot *blockstorage.Snapshot) error {
- volID, snapshotID, err := SplitSnapshotFullID(snapshot.ID)
- if err != nil {
- return errors.Wrap(err, "Cannot infer volume ID from full snapshot ID")
- }
- return wait.PollUntilContextTimeout(ctx, time.Second, defaultRetryLimit, false, func(context.Context) (bool, error) {
- log.Debug().Print("SnapshotDelete", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- task, lerr := p.Gom.DeleteSnapshot(ctx, vimID(volID), vimID(snapshotID))
- if lerr != nil {
- if soap.IsSoapFault(lerr) {
- soapFault := soap.ToSoapFault(lerr)
- receivedFault := soapFault.Detail.Fault
- _, ok := receivedFault.(types.NotFound)
- if ok {
- log.Debug().Print("The FCD id was not found in VC during deletion, assuming success", field.M{"err": lerr, "VolumeID": volID, "SnapshotID": snapshotID})
- return true, nil
- }
- }
- return false, errors.Wrap(lerr, "Failed to create a task for the DeleteSnapshot invocation on an IVD Protected Entity")
- }
- log.Debug().Print("Started SnapshotDelete task", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- _, lerr = task.Wait(ctx, vmWareTimeout)
- if lerr != nil {
- // The following error handling was pulled from https://github.com/vmware-tanzu/astrolabe/blob/91eeed4dcf77edd1387a25e984174f159d66fedb/pkg/ivd/ivd_protected_entity.go#L433
- if soap.IsVimFault(lerr) {
- switch soap.ToVimFault(lerr).(type) {
- case *types.InvalidArgument:
- log.Error().WithError(lerr).Print("Disk doesn't have given snapshot due to the snapshot stamp being " +
- "removed in the previous DeleteSnapshot operation which failed with an InvalidState fault. It " +
- "will be resolved by the next snapshot operation on the same VM. Will NOT retry")
- return true, nil
- case *types.NotFound:
- log.Error().WithError(lerr).Print("There is a temporary catalog mismatch due to a race condition with " +
- "one another concurrent DeleteSnapshot operation. It will be resolved by the next " +
- "consolidateDisks operation on the same VM. Will NOT retry")
- return true, nil
- case *types.InvalidState:
- log.Error().WithError(lerr).Print("There is some operation, other than this DeleteSnapshot invocation, on the same VM still being protected by its VM state. Will retry")
- return false, nil
- case *types.TaskInProgress:
- log.Error().WithError(lerr).Print("There is some other InProgress operation on the same VM. Will retry")
- return false, nil
- case *types.FileLocked:
- log.Error().WithError(lerr).Print("An error occurred while consolidating disks: Failed to lock the file. Will retry")
- return false, nil
- }
- }
- return false, errors.Wrap(lerr, "Failed to wait on task")
- }
- log.Debug().Print("SnapshotDelete task complete", field.M{"VolumeID": volID, "SnapshotID": snapshotID})
- err = p.deleteSnapshotTags(ctx, snapshot)
- if err != nil {
- return false, errors.Wrap(err, "Failed to delete snapshot tags")
- }
- return true, nil
- })
-}
-
-// SnapshotGet is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotGet(ctx context.Context, id string) (*blockstorage.Snapshot, error) {
- volID, snapshotID, err := SplitSnapshotFullID(id)
- if err != nil {
- return nil, errors.Wrap(err, "Cannot infer volume ID from full snapshot ID")
- }
- log.Debug().Print("RetrieveSnapshotInfo:" + volID)
- results, err := p.Gom.RetrieveSnapshotInfo(ctx, vimID(volID))
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshot info")
- }
- log.Debug().Print("RetrieveSnapshotInfo done:" + volID)
-
- for _, result := range results {
- if result.Id.Id == snapshotID {
- snapshot, err := convertFromObjectToSnapshot(&result, volID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to convert object to snapshot")
- }
- snapID := vimID(snapshotID)
- log.Debug().Print("RetrieveMetadata: " + volID + "," + snapshotID)
- kvs, err := p.Gom.RetrieveMetadata(ctx, vimID(volID), &snapID, "")
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshot metadata")
- }
- log.Debug().Print("RetrieveMetadata done: " + volID + "," + snapshotID)
- tags := convertKeyValueToTags(kvs)
- additionalTags, err := p.getSnapshotTags(ctx, id)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshot tags")
- }
- tags = append(tags, additionalTags...)
- snapshot.Tags = tags
- return snapshot, nil
- }
- }
- return nil, errors.New("Failed to find snapshot")
-}
-
-// SetTags is part of blockstorage.Provider
-func (p *FcdProvider) SetTags(ctx context.Context, resource interface{}, tags map[string]string) error {
- switch r := resource.(type) {
- case *blockstorage.Volume:
- return p.setTagsVolume(ctx, r, tags)
- case *blockstorage.Snapshot:
- return p.setSnapshotTags(ctx, r, tags)
- default:
- return errors.New("Unsupported type for resource")
- }
-}
-
-func (p *FcdProvider) setTagsVolume(ctx context.Context, volume *blockstorage.Volume, tags map[string]string) error {
- if volume == nil {
- return errors.New("Empty volume")
- }
- task, err := p.Gom.UpdateMetadata(ctx, vimID(volume.ID), convertTagsToKeyValue(tags), nil)
- if err != nil {
- return errors.Wrap(err, "Failed to update metadata")
- }
- _, err = task.Wait(ctx, vmWareTimeout)
- if err != nil {
- return errors.Wrap(err, "Failed to wait on task")
- }
- return nil
-}
-
-// GetOrCreateCategory takes a category name and attempts to get or create the category
-// it returns the category ID.
-func (p *FcdProvider) GetOrCreateCategory(ctx context.Context, categoryName string) (string, error) {
- id, err := p.GetCategoryID(ctx, categoryName)
- if err != nil {
- if strings.Contains(err.Error(), "404 Not Found") {
- id, err := p.tagManager.CreateCategory(ctx, &vapitags.Category{
- Name: categoryName,
- Cardinality: "SINGLE",
- })
- if err != nil {
- return "", errors.Wrap(err, "Failed to create category")
- }
- return id, nil
- }
- return "", err
- }
- return id, nil
-}
-
-// GetCategoryID takes a category name and returns the category ID if it finds it.
-func (p *FcdProvider) GetCategoryID(ctx context.Context, categoryName string) (string, error) {
- cat, err := p.tagManager.GetCategory(ctx, categoryName)
- if err != nil {
- return "", errors.Wrap(err, "Failed to find category")
- }
- return cat.ID, nil
-}
-
-// SnapshotsList is part of blockstorage.Provider
-func (p *FcdProvider) SnapshotsList(ctx context.Context, tags map[string]string) ([]*blockstorage.Snapshot, error) {
- if filterStr := generateSnapshotDescription(tags); filterStr != "" {
- volumeIDs := strings.Split(tags[VolumeIDListTag], ",")
- if len(volumeIDs) == 0 {
- return nil, errors.New("vSphere can't list by description without list of volumes. Cannot list snapshots")
- }
- return p.snapshotsListByDescription(ctx, volumeIDs, filterStr)
- }
- return p.snapshotsListByTag(ctx, tags)
-}
-
-func (p *FcdProvider) createSnapshotAndWaitForCompletion(volume blockstorage.Volume, ctx context.Context, description string) (types.AnyType, error) {
- log.Debug().Print("CreateSnapshot", field.M{"VolumeID": volume.ID})
- task, err := p.Gom.CreateSnapshot(ctx, vimID(volume.ID), description)
- if err != nil {
- return nil, errors.Wrap(err, "CreateSnapshot task creation failure")
- }
- log.Debug().Print("Started CreateSnapshot task", field.M{"VolumeID": volume.ID})
- return task.Wait(ctx, vmWareTimeout)
-}
-
-func (p *FcdProvider) getCreatedSnapshotID(ctx context.Context, description string, volID string, notEarlierThan time.Time) *types.ID {
- var filteredSns []*blockstorage.Snapshot
- sns, err := p.snapshotsListByDescription(ctx, []string{volID}, description)
- if err != nil {
- log.Error().WithError(err).Print("Failed to list when checking failed creation")
- return nil
- }
-
- for _, sn := range sns {
- if notEarlierThan.Before((time.Time)(sn.CreationTime)) {
- filteredSns = append(filteredSns, sn)
- }
- }
-
- if len(filteredSns) == 1 {
- _, snapID, err := SplitSnapshotFullID(filteredSns[0].ID)
- if err != nil {
- log.Error().WithError(err)
- return nil
- }
- return &types.ID{
- Id: snapID,
- }
- }
-
- if len(filteredSns) > 1 {
- log.Error().Print(fmt.Sprintf("More than one snapshot was found, IDs: %s", strings.Join(getSnapshotsIDs(filteredSns), ",")))
- }
- return nil
-}
-
-func generateSnapshotDescription(tags map[string]string) string {
- var tagsAsStr []string
- for name, value := range tags {
- if strings.HasPrefix(name, DescriptionTag) {
- tagsAsStr = append(tagsAsStr, fmt.Sprintf("%s:%s", name, value))
- }
- }
- return strings.Join(tagsAsStr, ",")
-}
-
-func getTagsWithoutDescription(tags map[string]string) map[string]string {
- result := make(map[string]string, len(tags))
- for name, value := range tags {
- if !strings.HasPrefix(name, DescriptionTag) {
- result[name] = value
- }
- }
- return result
-}
-
-func getSnapshotsIDs(snapshots []*blockstorage.Snapshot) []string {
- result := make([]string, 0, len(snapshots))
- for _, snapshot := range snapshots {
- result = append(result, snapshot.ID)
- }
- return result
-}
-
-// snapshotTag is the struct that will be used to create vmware tags
-// the tags are of the form volid:snapid:tag:value
-// these tags are assigned to a predefined category that is initialized by the FcdProvider
-type snapshotTag struct {
- volid string
- snapid string
- key string
- value string
-}
-
-func (t *snapshotTag) String() string {
- volid := strings.ReplaceAll(t.volid, ":", "-")
- snapid := strings.ReplaceAll(t.snapid, ":", "-")
- key := strings.ReplaceAll(t.key, ":", "-")
- value := strings.ReplaceAll(t.value, ":", "-")
- return fmt.Sprintf("%s:%s:%s:%s", volid, snapid, key, value)
-}
-
-func (t *snapshotTag) Parse(tag string) error {
- parts := strings.Split(tag, ":")
- if len(parts) != 4 {
- return errors.Errorf("Malformed tag (%s)", tag)
- }
- t.volid, t.snapid, t.key, t.value = parts[0], parts[1], parts[2], parts[3]
- return nil
-}
-
-// setSnapshotTags sets tags for a snapshot
-func (p *FcdProvider) setSnapshotTags(ctx context.Context, snapshot *blockstorage.Snapshot, tags map[string]string) error {
- if p.categoryID == "" {
- log.Debug().Print("vSphere snapshot tagging is disabled")
- return nil
- }
- if snapshot == nil {
- return errors.New("Empty snapshot")
- }
- volID, snapID, err := SplitSnapshotFullID(snapshot.ID)
- if err != nil {
- return errors.Wrap(err, "Cannot infer volumeID and snapshotID from full snapshot ID")
- }
-
- for k, v := range tags {
- tag := &snapshotTag{volID, snapID, k, v}
- _, err = p.tagManager.CreateTag(ctx, &vapitags.Tag{
- CategoryID: p.categoryID,
- Name: tag.String(),
- })
- if err != nil && !strings.Contains(err.Error(), "ALREADY_EXISTS") {
- return errors.Wrapf(err, "Failed to create tag (%s) for categoryID (%s) ", tag, p.categoryID)
- }
- }
- return nil
-}
-
-func (p *FcdProvider) deleteSnapshotTags(ctx context.Context, snapshot *blockstorage.Snapshot) error {
- if p.categoryID == "" {
- log.Debug().Print("vSphere snapshot tagging is disabled (categoryID not set). Cannot list snapshots")
- return nil
- }
- if snapshot == nil {
- return errors.New("Empty snapshot")
- }
- volID, snapID, err := SplitSnapshotFullID(snapshot.ID)
- if err != nil {
- return errors.Wrap(err, "Cannot infer volumeID and snapshotID from full snapshot ID")
- }
- categoryTags, err := p.tagManager.GetTagsForCategory(ctx, p.categoryID)
- if err != nil {
- return errors.Wrap(err, "Failed to list tags")
- }
- for _, tag := range categoryTags {
- parsedTag := &snapshotTag{}
- err := parsedTag.Parse(tag.Name)
- if err != nil {
- return errors.Wrapf(err, "Failed to parse tag (%s)", tag.Name)
- }
- if parsedTag.snapid == snapID && parsedTag.volid == volID {
- err := p.tagManager.DeleteTag(ctx, &tag)
- if err != nil {
- return errors.Wrapf(err, "Failed to delete tag (%s)", tag.Name)
- }
- }
- }
- return nil
-}
-
-// VolumesList is part of blockstorage.Provider
-func (p *FcdProvider) VolumesList(ctx context.Context, tags map[string]string, zone string) ([]*blockstorage.Volume, error) {
- return nil, errors.New("Not implemented")
-}
-
-func (p *FcdProvider) getSnapshotTags(ctx context.Context, fullSnapshotID string) ([]*blockstorage.KeyValue, error) {
- if p.categoryID == "" {
- log.Debug().Print("vSphere snapshot tagging is disabled (categoryID not set). Cannot get snapshot tags")
- return nil, nil
- }
- categoryTags, err := p.tagManager.GetTagsForCategory(ctx, p.categoryID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to list tags")
- }
- return p.getTagsFromSnapshotID(categoryTags, fullSnapshotID)
-}
-
-func (p *FcdProvider) snapshotsListByDescription(ctx context.Context, volumeIDs []string, filterStr string) ([]*blockstorage.Snapshot, error) {
- var result []*blockstorage.Snapshot
- for _, volID := range volumeIDs {
- snapshots, _ := p.Gom.RetrieveSnapshotInfo(ctx, vimID(volID))
- for _, snapshot := range snapshots {
- if snapshot.Description == filterStr {
- sn, err := convertFromObjectToSnapshot(&snapshot, volID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to convert object to snapshot")
- }
- result = append(result, sn)
- }
- }
- }
-
- return result, nil
-}
-
-func (p *FcdProvider) snapshotsListByTag(ctx context.Context, tags map[string]string) ([]*blockstorage.Snapshot, error) {
- if p.categoryID == "" {
- log.Debug().Print("vSphere snapshot tagging is disabled (categoryID not set). Cannot list snapshots")
- return nil, nil
- }
-
- categoryTags, err := p.tagManager.GetTagsForCategory(ctx, p.categoryID)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to list tags")
- }
-
- snapshotIDs, err := p.getSnapshotIDsFromTags(categoryTags, tags)
- if err != nil {
- return nil, errors.Wrap(err, "Failed to get snapshotIDs from tags")
- }
-
- var snapshots []*blockstorage.Snapshot
- if len(snapshotIDs) > 0 {
- for _, snapshotID := range snapshotIDs {
- snapshot, err := p.SnapshotGet(ctx, snapshotID)
- if err != nil {
- return nil, err
- }
- snapshots = append(snapshots, snapshot)
- }
- }
- return snapshots, nil
-}
-
-func (p *FcdProvider) getTagsFromSnapshotID(categoryTags []vapitags.Tag, fullSnapshotID string) ([]*blockstorage.KeyValue, error) {
- tags := map[string]string{}
- for _, catTag := range categoryTags {
- parsedTag := &snapshotTag{}
- if err := parsedTag.Parse(catTag.Name); err != nil {
- return nil, errors.Wrapf(err, "Failed to parse tag")
- }
- snapId := SnapshotFullID(parsedTag.volid, parsedTag.snapid)
- if snapId == fullSnapshotID {
- tags[parsedTag.key] = parsedTag.value
- }
- }
- return blockstorage.MapToKeyValue(tags), nil
-}
-
-func (p *FcdProvider) getSnapshotIDsFromTags(categoryTags []vapitags.Tag, tags map[string]string) ([]string, error) {
- snapshotTagMap := map[string]map[string]string{}
- for _, catTag := range categoryTags {
- parsedTag := &snapshotTag{}
- if err := parsedTag.Parse(catTag.Name); err != nil {
- return nil, errors.Wrapf(err, "Failed to parse tag")
- }
- snapId := SnapshotFullID(parsedTag.volid, parsedTag.snapid)
- if _, ok := snapshotTagMap[snapId]; !ok {
- snapshotTagMap[snapId] = map[string]string{}
- }
- snapshotTagMap[snapId][parsedTag.key] = parsedTag.value
- }
-
- snapshotIDs := []string{}
- for snapshotID, snapshotTags := range snapshotTagMap {
- tagsMatch := true
- for k, v := range tags {
- if val, ok := snapshotTags[k]; !ok || val != v {
- tagsMatch = false
- break
- }
- }
- if tagsMatch {
- snapshotIDs = append(snapshotIDs, snapshotID)
- }
- }
- return snapshotIDs, nil
-}
-
-func getEnvAsIntOrDefault(envKey string, def int) int {
- if v, ok := os.LookupEnv(envKey); ok {
- iv, err := strconv.Atoi(v)
- if err == nil && iv > 0 {
- return iv
- }
- log.Debug().Print("Using default timeout value for vSphere because of invalid environment variable", field.M{"envVar": v})
- }
-
- return def
-}
-
-type tagManager interface {
- GetCategory(ctx context.Context, id string) (*vapitags.Category, error)
- CreateCategory(ctx context.Context, category *vapitags.Category) (string, error)
- CreateTag(ctx context.Context, tag *vapitags.Tag) (string, error)
- GetTagsForCategory(ctx context.Context, id string) ([]vapitags.Tag, error)
- DeleteTag(ctx context.Context, tag *vapitags.Tag) error
-}
-
-// Helper to parse an error code returned by the govmomi repo.
-type govmomiError struct {
- err error
-}
-
-func (ge govmomiError) Format() string {
- msgs := ge.ExtractMessages()
- switch len(msgs) {
- case 0:
- return ""
- case 1:
- return msgs[0]
- }
- return fmt.Sprintf("[%s]", strings.Join(msgs, "; "))
-}
-
-//nolint:gocognit,nestif
-func (ge govmomiError) ExtractMessages() []string {
- err := ge.err
-
- if err == nil {
- return nil
- }
-
- msgs := []string{}
- if reason := err.Error(); reason != "" {
- msgs = append(msgs, reason)
- }
-
- // unwrap to a type handled
- foundHandledErrorType := false
- for err != nil && !foundHandledErrorType {
- switch err.(type) {
- case govmomitask.Error:
- foundHandledErrorType = true
- default:
- switch {
- case soap.IsSoapFault(err):
- foundHandledErrorType = true
- case soap.IsVimFault(err):
- foundHandledErrorType = true
- default:
- err = errors.Unwrap(err)
- }
- }
- }
-
- if err != nil {
- var faultMsgs []types.LocalizableMessage
- switch e := err.(type) {
- case govmomitask.Error:
- if e.Description != nil {
- msgs = append(msgs, e.Description.Message)
- }
- faultMsgs = e.LocalizedMethodFault.Fault.GetMethodFault().FaultMessage
- default:
- if soap.IsSoapFault(err) {
- detail := soap.ToSoapFault(err).Detail.Fault
- if f, ok := detail.(types.BaseMethodFault); ok {
- faultMsgs = f.GetMethodFault().FaultMessage
- }
- } else if soap.IsVimFault(err) {
- f := soap.ToVimFault(err)
- faultMsgs = f.GetMethodFault().FaultMessage
- }
- }
-
- for _, m := range faultMsgs {
- if m.Message != "" && !strings.HasPrefix(m.Message, "[context]") {
- msgs = append(msgs, fmt.Sprintf("%s (%s)", m.Message, m.Key))
- }
- for _, a := range m.Arg {
- msgs = append(msgs, fmt.Sprintf("%s", a.Value))
- }
- }
- }
-
- return msgs
-}
-
-func (ge govmomiError) Matches(pat *regexp.Regexp) bool {
- for _, m := range ge.ExtractMessages() {
- if pat.MatchString(m) {
- return true
- }
- }
-
- return false
-}
diff --git a/pkg/blockstorage/vmware/vmware_manual_test.go b/pkg/blockstorage/vmware/vmware_manual_test.go
deleted file mode 100644
index 1fd20ebba9..0000000000
--- a/pkg/blockstorage/vmware/vmware_manual_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package vmware
-
-import (
- "context"
-
- "github.com/gofrs/uuid"
- "gopkg.in/check.v1"
-)
-
-type VmwareManSuite struct{}
-
-var _ = check.Suite(&VmwareManSuite{})
-
-func (s *VmwareManSuite) TestCreateAndListSnapshots(c *check.C) {
- c.Skip("manual testing")
- volumeID := "55c3e39b-95b0-40d1-aaed-ea11be829fa6"
- provider, _ := NewProvider(map[string]string{
- VSphereEndpointKey: "",
- VSphereUsernameKey: "",
- VSpherePasswordKey: "",
- })
- ftpProvider := provider.(*FcdProvider)
-
- ftpProvider.SetCategoryID("K10:0c66728a-dd0d-11ec-9939-ca6a7623d809")
- ctx := context.Background()
-
- guid1, _ := uuid.NewV1()
- guid2, _ := uuid.NewV1()
- tags := map[string]string{
- DescriptionTag: guid1.String(),
- "manifest": guid1.String(),
- VolumeIDListTag: volumeID,
- }
-
- volume, _ := provider.VolumeGet(ctx, volumeID, "")
- snapshot1, _ := provider.SnapshotCreate(ctx, *volume, map[string]string{"manifest": guid1.String(), DescriptionTag: guid2.String()})
- snapshot2, _ := provider.SnapshotCreate(ctx, *volume, tags)
-
- foundSnapshotsByID, _ := provider.SnapshotsList(ctx, tags)
- foundAllSnapshots, _ := provider.SnapshotsList(ctx, map[string]string{"manifest": guid1.String()})
-
- c.Assert(len(foundSnapshotsByID), check.Equals, 1)
- c.Assert(len(foundAllSnapshots), check.Equals, 2)
- c.Assert(snapshot2.ID, check.Equals, foundSnapshotsByID[0].ID)
-
- err := provider.SnapshotDelete(ctx, snapshot2)
- c.Assert(err, check.IsNil)
- err = provider.SnapshotDelete(ctx, snapshot1)
- c.Assert(err, check.IsNil)
-
- foundAllSnapshots, _ = provider.SnapshotsList(ctx, map[string]string{"manifest": guid1.String()})
- c.Assert(len(foundAllSnapshots), check.Equals, 0)
-}
diff --git a/pkg/blockstorage/vmware/vmware_test.go b/pkg/blockstorage/vmware/vmware_test.go
deleted file mode 100644
index af8b9f3439..0000000000
--- a/pkg/blockstorage/vmware/vmware_test.go
+++ /dev/null
@@ -1,547 +0,0 @@
-package vmware
-
-import (
- "bytes"
- "context"
- "fmt"
- "os"
- "sort"
- "testing"
- "time"
-
- "github.com/pkg/errors"
- govmomitask "github.com/vmware/govmomi/task"
- vapitags "github.com/vmware/govmomi/vapi/tags"
- "github.com/vmware/govmomi/vim25/soap"
- "github.com/vmware/govmomi/vim25/types"
- "github.com/vmware/govmomi/vim25/xml"
- vslmtypes "github.com/vmware/govmomi/vslm/types"
- . "gopkg.in/check.v1"
-
- "github.com/kanisterio/kanister/pkg/blockstorage"
-)
-
-func Test(t *testing.T) { TestingT(t) }
-
-type VMWareSuite struct{}
-
-var _ = Suite(&VMWareSuite{})
-
-func (s *VMWareSuite) TestURLParse(c *C) {
- for _, tc := range []struct {
- config map[string]string
- errCheck Checker
- expErrString string
- }{
- {
- config: map[string]string{},
- errCheck: NotNil,
- expErrString: "Failed to find VSphere endpoint value",
- },
- {
- config: map[string]string{
- VSphereEndpointKey: "ep",
- },
- errCheck: NotNil,
- expErrString: "Failed to find VSphere username value",
- },
- {
- config: map[string]string{
- VSphereEndpointKey: "ep",
- VSphereUsernameKey: "user",
- },
- errCheck: NotNil,
- expErrString: "Failed to find VSphere password value",
- },
- { // until we can run against a VIM setup this will always fail.
- config: map[string]string{
- VSphereEndpointKey: "ep",
- VSphereUsernameKey: "user",
- VSpherePasswordKey: "pass",
- },
- errCheck: NotNil,
- expErrString: "Failed to create VIM client",
- },
- } {
- _, err := NewProvider(tc.config)
- c.Check(err, tc.errCheck)
- if err != nil {
- c.Assert(err, ErrorMatches, ".*"+tc.expErrString+".*")
- }
- }
-}
-
-func (s *VMWareSuite) TestIsParaVirtualized(c *C) {
- // the constructor needs VIM so just check the parsing of the config map.
-
- config := map[string]string{}
- c.Assert(false, Equals, configIsParaVirtualized(config))
- config[VSphereIsParaVirtualizedKey] = "false"
- c.Assert(false, Equals, configIsParaVirtualized(config))
- config[VSphereIsParaVirtualizedKey] = "true"
- c.Assert(true, Equals, configIsParaVirtualized(config))
- config[VSphereIsParaVirtualizedKey] = "TRUE"
- c.Assert(true, Equals, configIsParaVirtualized(config))
- config[VSphereIsParaVirtualizedKey] = "1"
- c.Assert(true, Equals, configIsParaVirtualized(config))
-
- fcd := &FcdProvider{}
- c.Assert(false, Equals, fcd.IsParaVirtualized())
- fcd.isParaVirtualized = true
- c.Assert(true, Equals, fcd.IsParaVirtualized())
-
- // failed operations
- v, err := fcd.VolumeCreateFromSnapshot(context.Background(), blockstorage.Snapshot{}, nil)
- c.Assert(true, Equals, errors.Is(err, ErrNotSupportedWithParaVirtualizedVolumes))
- c.Assert(v, IsNil)
-}
-
-func (s *VMWareSuite) TestTimeoutEnvSetting(c *C) {
- tempEnv := os.Getenv(vmWareTimeoutMinEnv)
- os.Unsetenv(vmWareTimeoutMinEnv)
- timeout := time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
- c.Assert(timeout, Equals, defaultWaitTime)
-
- os.Setenv(vmWareTimeoutMinEnv, "7")
- timeout = time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
- c.Assert(timeout, Equals, 7*time.Minute)
-
- os.Setenv(vmWareTimeoutMinEnv, "badValue")
- timeout = time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
- c.Assert(timeout, Equals, defaultWaitTime)
-
- os.Setenv(vmWareTimeoutMinEnv, "-1")
- timeout = time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
- c.Assert(timeout, Equals, defaultWaitTime)
-
- os.Setenv(vmWareTimeoutMinEnv, "0")
- timeout = time.Duration(getEnvAsIntOrDefault(vmWareTimeoutMinEnv, int(defaultWaitTime/time.Minute))) * time.Minute
- c.Assert(timeout, Equals, defaultWaitTime)
-
- timeout = time.Duration(getEnvAsIntOrDefault("someotherenv", 5)) * time.Minute
- c.Assert(timeout, Equals, 5*time.Minute)
-
- os.Setenv(vmWareTimeoutMinEnv, tempEnv)
-}
-
-func (s *VMWareSuite) TestGetSnapshotIDsFromTags(c *C) {
- for _, tc := range []struct {
- catTags []vapitags.Tag
- tags map[string]string
- errChecker Checker
- snapIDs []string
- }{
- {
- catTags: []vapitags.Tag{
- {Name: "v1:s1:k1:v1"},
- {Name: "v1:s1:k2:v2"},
- {Name: "v1:s2:k1:v1"},
- },
- tags: map[string]string{
- "k1": "v1",
- "k2": "v2",
- },
- snapIDs: []string{"v1:s1"},
- errChecker: IsNil,
- },
- {
- catTags: []vapitags.Tag{
- {Name: "v1:s1:k1:v1"},
- {Name: "v1:s1:k2:v2"},
- {Name: "v1:s2:k1:v1"},
- },
- tags: map[string]string{
- "k1": "v1",
- },
- snapIDs: []string{"v1:s1", "v1:s2"},
- errChecker: IsNil,
- },
- {
- catTags: []vapitags.Tag{
- {Name: "v1:s1:k1:v1"},
- {Name: "v1:s1:k2:v2"},
- {Name: "v1:s2:k1:v1"},
- },
- snapIDs: []string{"v1:s1", "v1:s2"},
- errChecker: IsNil,
- },
- {
- catTags: []vapitags.Tag{
- {Name: "v1:s1k1:v1"},
- },
- tags: map[string]string{
- "k1": "v1",
- },
- errChecker: NotNil,
- },
- } {
- fp := &FcdProvider{}
- snapIDs, err := fp.getSnapshotIDsFromTags(tc.catTags, tc.tags)
- c.Assert(err, tc.errChecker)
- if tc.errChecker == IsNil {
- sort.Strings(snapIDs)
- sort.Strings(tc.snapIDs)
- c.Assert(snapIDs, DeepEquals, tc.snapIDs)
- }
- }
-}
-
-func (s *VMWareSuite) TestSetTagsSnapshot(c *C) {
- ctx := context.Background()
- for _, tc := range []struct {
- catID string
- snapshot *blockstorage.Snapshot
- tags map[string]string
- errChecker Checker
- expNumCreates int
-
- errCreateTag error
- }{
- { // success
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- tags: map[string]string{
- "t1": "v1",
- "t2": "v2",
- },
- expNumCreates: 2,
- errChecker: IsNil,
- },
- { // idempotent creates
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- tags: map[string]string{
- "t1": "v1",
- "t2": "v2",
- },
- expNumCreates: 2,
- errCreateTag: fmt.Errorf("ALREADY_EXISTS"),
- errChecker: IsNil,
- },
- { // create failure
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- tags: map[string]string{
- "t1": "v1",
- "t2": "v2",
- },
- expNumCreates: 2,
- errCreateTag: fmt.Errorf("bad create"),
- errChecker: NotNil,
- },
- { // malformed id
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volidsnapid"},
- errChecker: NotNil,
- },
- { // nil snapshot
- catID: "catid",
- errChecker: NotNil,
- },
- { // empty id, No error, not supported
- catID: "",
- errChecker: IsNil,
- },
- } {
- ftm := &fakeTagManager{
- errCreateTag: tc.errCreateTag,
- }
- provider := &FcdProvider{
- categoryID: tc.catID,
- tagManager: ftm,
- }
- err := provider.setSnapshotTags(ctx, tc.snapshot, tc.tags)
- c.Assert(err, tc.errChecker)
- if tc.errChecker == IsNil {
- c.Assert(ftm.numCreates, Equals, tc.expNumCreates)
- }
- }
-}
-
-func (s *VMWareSuite) TestDeleteTagsSnapshot(c *C) {
- ctx := context.Background()
- for _, tc := range []struct {
- catID string
- snapshot *blockstorage.Snapshot
- errChecker Checker
- expNumDeletes int
-
- retGetTagsForCategory []vapitags.Tag
- errGetTagsForCategory error
- errDeleteTag error
- }{
- { // success deleting tags
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- retGetTagsForCategory: []vapitags.Tag{
- {Name: "volid:snapid:t1:v1"},
- {Name: "volid:snapid:t2:v2"},
- {Name: "volid:snapid2:t1:v1"},
- },
- expNumDeletes: 2,
- errChecker: IsNil,
- },
- { // error deleting tags
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- retGetTagsForCategory: []vapitags.Tag{
- {Name: "volid:snapid:t1:v1"},
- {Name: "volid:snapid:t2:v2"},
- },
- errDeleteTag: fmt.Errorf("Failed to delete tag"),
- errChecker: NotNil,
- },
- { // error parsing tags
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- retGetTagsForCategory: []vapitags.Tag{
- {Name: "volid:snapidt1v1"},
- {Name: "volid:snapid:t2:v2"},
- },
- errChecker: NotNil,
- },
- { // error fetching tags
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volid:snapid"},
- errGetTagsForCategory: fmt.Errorf("Failed to get tags"),
- errChecker: NotNil,
- },
- { // malformed id
- catID: "catid",
- snapshot: &blockstorage.Snapshot{ID: "volidsnapid"},
- errChecker: NotNil,
- },
- { // nil snapshot
- catID: "catid",
- errChecker: NotNil,
- },
- { // empty id, No error, not supported
- catID: "",
- errChecker: IsNil,
- },
- } {
- ftm := &fakeTagManager{
- retGetTagsForCategory: tc.retGetTagsForCategory,
- errGetTagsForCategory: tc.errGetTagsForCategory,
- errDeleteTag: tc.errDeleteTag,
- }
- provider := &FcdProvider{
- categoryID: tc.catID,
- tagManager: ftm,
- }
- err := provider.deleteSnapshotTags(ctx, tc.snapshot)
- c.Assert(err, tc.errChecker)
- if tc.errChecker == IsNil {
- c.Assert(ftm.numDeletes, Equals, tc.expNumDeletes)
- }
- }
-}
-
-func (s *VMWareSuite) TestGetSnapshotTags(c *C) {
- ctx := context.Background()
- for _, tc := range []struct {
- snapshotID string
- catID string
- categoryTags []vapitags.Tag
- expNumTags int
- errGetTags error
- errChecker Checker
- }{
- { // success
- snapshotID: "v1:s1",
- categoryTags: []vapitags.Tag{
- {Name: "v1:s1:t1:v1"},
- {Name: "v1:s1:t2:v2"},
- {Name: "v1:s2:t3:v3"},
- {Name: "v3:s2:t4:v4"},
- },
- errChecker: IsNil,
- catID: "something",
- expNumTags: 2,
- },
- { // bad tag
- snapshotID: "v1:s1",
- categoryTags: []vapitags.Tag{
- {Name: "v1:s1:t1:v1"},
- {Name: "v1:s1:t2:v2"},
- {Name: "v1:s2:t3:v3"},
- {Name: "v3:s2t4:v4"},
- },
- catID: "something",
- errChecker: NotNil,
- },
- { // bad tag
- snapshotID: "v1:s1",
- categoryTags: []vapitags.Tag{},
- errGetTags: errors.New("get tags error"),
- errChecker: NotNil,
- catID: "something",
- },
- { // empty cat id
- errChecker: IsNil,
- catID: "",
- expNumTags: 0,
- },
- } {
- ftm := &fakeTagManager{
- retGetTagsForCategory: tc.categoryTags,
- errGetTagsForCategory: tc.errGetTags,
- }
- provider := &FcdProvider{
- categoryID: tc.catID,
- tagManager: ftm,
- }
- tags, err := provider.getSnapshotTags(ctx, tc.snapshotID)
- c.Assert(err, tc.errChecker)
- if tc.errChecker == IsNil {
- c.Assert(len(tags), Equals, tc.expNumTags)
- }
- }
-}
-
-// An XML trace from `govc disk.snapshot.ls` with the VslmSyncFault
-var (
- vslmSyncFaultReason = "Change tracking invalid or disk in use: api = DiskLib_BlockTrackGetEpoch, path->CValue() = /vmfs/volumes/vsan:52731cd109496ced-173f8e8aec7c6828/dc6d0c61-ec84-381f-2fa3-000c29e75b7f/4e1e7c4619a34919ae1f28fbb53fcd70-000008.vmdk"
-
- vslmSyncFaultReasonEsc = "Change tracking invalid or disk in use: api = DiskLib_BlockTrackGetEpoch, path->CValue() = /vmfs/volumes/vsan:52731cd109496ced-173f8e8aec7c6828/dc6d0c61-ec84-381f-2fa3-000c29e75b7f/4e1e7c4619a34919ae1f28fbb53fcd70-000008.vmdk"
-
- vslmSyncFaultString = "A general system error occurred: " + vslmSyncFaultReason
- vslmSyncFaultStringEsc = "A general system error occurred: " + vslmSyncFaultReasonEsc
-
- vslmSyncFaultXML = `
- ServerFaultCode
- ` + vslmSyncFaultStringEsc + `
-
-
- ` + vslmSyncFaultReasonEsc + `
-
-
- `
-
- vslmSyncFaultXMLEnv = `
-
- ` + vslmSyncFaultXML + `
- `
-)
-
-func (s *VMWareSuite) TestFormatGovmomiError(c *C) {
- // basic soap fault
- fault := &soap.Fault{
- Code: "soap-fault",
- String: "fault string",
- }
- soapFaultErr := soap.WrapSoapFault(fault)
- c.Assert(govmomiError{soapFaultErr}.Format(), Equals, "soap-fault: fault string")
- c.Assert(govmomiError{errors.Wrap(soapFaultErr, "outer wrapper")}.Format(), Equals, "outer wrapper: soap-fault: fault string")
-
- // Experiment with a real fault XML to figure out how to decode an error.
- // (adapted from govmomi/vim25/methods/fault_test.go)
- type TestBody struct {
- Fault *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
- }
- body := TestBody{}
- env := soap.Envelope{Body: &body}
- dec := xml.NewDecoder(bytes.NewReader([]byte(vslmSyncFaultXMLEnv)))
- dec.TypeFunc = types.TypeFunc()
- err := dec.Decode(&env)
- c.Assert(err, IsNil)
- c.Assert(body.Fault, NotNil)
-
- err = soap.WrapSoapFault(body.Fault)
- c.Assert(soap.IsSoapFault(err), Equals, true)
- c.Assert(err.Error(), Equals, "ServerFaultCode: "+vslmSyncFaultString) // details present
-
- vimFault := &types.VimFault{
- MethodFault: types.MethodFault{
- FaultCause: &types.LocalizedMethodFault{
- LocalizedMessage: err.Error(),
- },
- },
- }
- err = soap.WrapVimFault(vimFault)
- c.Assert(soap.IsVimFault(err), Equals, true)
- c.Assert(err.Error(), Equals, "VimFault") // lost the details
-
- // A vslmFault fault with details such as that returned by gom.SnapshotCreate when
- // a volume CTK file is moved. (Note: govc succeeds in this case but list will fail)
- vslmFaultValue := "(vmodl.fault.SystemError) {\n faultCause = null,\n faultMessage = null,\n reason = " + vslmSyncFaultReason + "}"
- vslmFault := &vslmtypes.VslmSyncFault{
- VslmFault: vslmtypes.VslmFault{
- MethodFault: types.MethodFault{
- FaultMessage: []types.LocalizableMessage{
- {
- Key: "com.vmware.pbm.pbmFault.locale",
- Arg: []types.KeyAnyValue{
- {
- Key: "summary",
- Value: vslmFaultValue,
- },
- },
- },
- },
- },
- },
- Id: &types.ID{},
- }
- c.Assert(vslmFault.GetMethodFault(), NotNil)
- c.Assert(vslmFault.GetMethodFault().FaultMessage, DeepEquals, vslmFault.FaultMessage)
-
- err = soap.WrapVimFault(vslmFault)
- c.Assert(err.Error(), Equals, "VslmSyncFault")
- c.Assert(govmomiError{err}.Format(), Equals, "["+err.Error()+"; "+vslmFaultValue+"]")
- c.Assert(govmomiError{errors.Wrap(err, "outer wrapper")}.Format(), Equals, "[outer wrapper: "+err.Error()+"; "+vslmFaultValue+"]")
-
- c.Assert(govmomiError{err}.Matches(reVslmSyncFaultFatal), Equals, true)
-
- // task errors
- te := govmomitask.Error{
- LocalizedMethodFault: &types.LocalizedMethodFault{
- Fault: vslmFault,
- },
- Description: &types.LocalizableMessage{
- Message: "description message",
- },
- }
- c.Assert(err.Error(), Equals, "VslmSyncFault")
- c.Assert(govmomiError{te}.Format(), Equals, "[description message; "+vslmFaultValue+"]")
- c.Assert(govmomiError{errors.Wrap(te, "outer wrapper")}.Format(), Equals, "[outer wrapper: ; description message; "+vslmFaultValue+"]")
-
- // normal error
- testError := errors.New("test-error")
- c.Assert(govmomiError{testError}.Format(), Equals, testError.Error())
-
- // nil
- c.Assert(govmomiError{nil}.Format(), Equals, "")
-}
-
-type fakeTagManager struct {
- retGetTagsForCategory []vapitags.Tag
- errGetTagsForCategory error
-
- numDeletes int
- errDeleteTag error
-
- numCreates int
- errCreateTag error
-}
-
-func (f *fakeTagManager) GetCategory(ctx context.Context, id string) (*vapitags.Category, error) {
- return nil, nil
-}
-func (f *fakeTagManager) CreateCategory(ctx context.Context, category *vapitags.Category) (string, error) {
- return "", nil
-}
-func (f *fakeTagManager) CreateTag(ctx context.Context, tag *vapitags.Tag) (string, error) {
- f.numCreates++
- return "", f.errCreateTag
-}
-func (f *fakeTagManager) GetTagsForCategory(ctx context.Context, id string) ([]vapitags.Tag, error) {
- return f.retGetTagsForCategory, f.errGetTagsForCategory
-}
-func (f *fakeTagManager) DeleteTag(ctx context.Context, tag *vapitags.Tag) error {
- f.numDeletes++
- return f.errDeleteTag
-}