diff --git a/test/e2e/framework/provider.go b/test/e2e/framework/provider.go index 986cec98181..b5cb16ceb69 100644 --- a/test/e2e/framework/provider.go +++ b/test/e2e/framework/provider.go @@ -21,7 +21,7 @@ import ( "os" "sync" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" clientset "k8s.io/client-go/kubernetes" ) @@ -97,6 +97,9 @@ type ProviderInterface interface { CreatePD(zone string) (string, error) DeletePD(pdName string) error + CreateShare() (string, string, error) + DeleteShare(accountName, shareName string) error + CreatePVSource(zone, diskName string) (*v1.PersistentVolumeSource, error) DeletePVSource(pvSource *v1.PersistentVolumeSource) error @@ -137,6 +140,14 @@ func (n NullProvider) DeleteNode(node *v1.Node) error { return fmt.Errorf("provider does not support DeleteNode") } +func (n NullProvider) CreateShare() (string, string, error) { + return "", "", fmt.Errorf("provider does not support volume creation") +} + +func (n NullProvider) DeleteShare(accountName, shareName string) error { + return fmt.Errorf("provider does not support volume deletion") +} + // CreatePD is a base implementation which creates PD. func (n NullProvider) CreatePD(zone string) (string, error) { return "", fmt.Errorf("provider does not support volume creation") diff --git a/test/e2e/framework/providers/aws/aws.go b/test/e2e/framework/providers/aws/aws.go index e5291115c3d..d00d4b29fe1 100644 --- a/test/e2e/framework/providers/aws/aws.go +++ b/test/e2e/framework/providers/aws/aws.go @@ -26,7 +26,7 @@ import ( "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/ec2" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/kubernetes/test/e2e/framework" e2epv "k8s.io/kubernetes/test/e2e/framework/pv" awscloud "k8s.io/legacy-cloud-providers/aws" @@ -93,6 +93,14 @@ func (p *Provider) DeleteNode(node *v1.Node) error { return err } +func (p *Provider) CreateShare() (string, string, error) { + return "", "", nil +} + +func (p *Provider) DeleteShare(accountName, shareName string) error { + return nil +} + // CreatePD creates a persistent volume on the specified availability zone func (p *Provider) CreatePD(zone string) (string, error) { client := newAWSClient(zone) diff --git a/test/e2e/framework/providers/azure/azure.go b/test/e2e/framework/providers/azure/azure.go index 70a39935299..fea723dee6b 100644 --- a/test/e2e/framework/providers/azure/azure.go +++ b/test/e2e/framework/providers/azure/azure.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/test/e2e/framework" "k8s.io/legacy-cloud-providers/azure" + "k8s.io/legacy-cloud-providers/azure/clients/fileclient" ) func init() { @@ -84,6 +85,36 @@ func (p *Provider) CreatePD(zone string) (string, error) { return p.azureCloud.CreateManagedDisk(volumeOptions) } +// CreateShare creates a share and return its account name and key. +func (p *Provider) CreateShare() (string, string, error) { + accountOptions := &azure.AccountOptions{ + Name: "", + Type: string(compute.StandardLRS), + ResourceGroup: p.azureCloud.ResourceGroup, + Location: p.azureCloud.GetLocation(), + EnableHTTPSTrafficOnly: true, + Tags: nil, + VirtualNetworkResourceIDs: nil, + } + + shareOptions := &fileclient.ShareOptions{ + Name: fmt.Sprintf("%s-%s", framework.TestContext.Prefix, string(uuid.NewUUID())), + RequestGiB: 1, + } + + a, b, c := p.azureCloud.CreateFileShare(accountOptions, shareOptions) + + return a, b, c +} + +func (p *Provider) DeleteShare(accountName, shareName string) error { + err := p.azureCloud.DeleteFileShare("", accountName, shareName) + if err != nil { + framework.Logf("failed to delete Azure File share %q: %v", shareName, err) + } + return err +} + // DeletePD deletes a persistent volume func (p *Provider) DeletePD(pdName string) error { if err := p.azureCloud.DeleteManagedDisk(pdName); err != nil { diff --git a/test/e2e/framework/providers/gce/gce.go b/test/e2e/framework/providers/gce/gce.go index 05b0b003c83..7db8d791b93 100644 --- a/test/e2e/framework/providers/gce/gce.go +++ b/test/e2e/framework/providers/gce/gce.go @@ -225,6 +225,14 @@ func (p *Provider) DeleteNode(node *v1.Node) error { return p.gceCloud.DeleteInstance(project, zone, node.Name) } +func (p *Provider) CreateShare() (string, string, error) { + return "", "", nil +} + +func (p *Provider) DeleteShare(accountName, shareName string) error { + return nil +} + // CreatePD creates a persistent volume func (p *Provider) CreatePD(zone string) (string, error) { pdName := fmt.Sprintf("%s-%s", framework.TestContext.Prefix, string(uuid.NewUUID())) diff --git a/test/e2e/framework/pv/pv.go b/test/e2e/framework/pv/pv.go index ed514d1d58c..44e1ce0718e 100644 --- a/test/e2e/framework/pv/pv.go +++ b/test/e2e/framework/pv/pv.go @@ -673,6 +673,14 @@ func createPDWithRetry(zone string) (string, error) { return "", err } +func CreateShare() (string, string, error) { + return framework.TestContext.CloudConfig.Provider.CreateShare() +} + +func DeleteShare(accountName, shareName string) error { + return framework.TestContext.CloudConfig.Provider.DeleteShare(accountName, shareName) +} + // CreatePDWithRetry creates PD with retry. func CreatePDWithRetry() (string, error) { return createPDWithRetry("") diff --git a/test/e2e/storage/drivers/in_tree.go b/test/e2e/storage/drivers/in_tree.go index d97c4f4b2b0..b321aecc488 100644 --- a/test/e2e/storage/drivers/in_tree.go +++ b/test/e2e/storage/drivers/in_tree.go @@ -2025,3 +2025,112 @@ func cleanUpVolumeServerWithSecret(f *framework.Framework, serverPod *v1.Pod, se framework.Logf("Server pod delete failed: %v", err) } } + +// Azure File +type azureFileDriver struct { + driverInfo storageframework.DriverInfo +} + +type azureFileVolume struct { + accountName string + shareName string + secretName string +} + +var _ storageframework.TestDriver = &azureFileDriver{} +var _ storageframework.PreprovisionedVolumeTestDriver = &azureFileDriver{} +var _ storageframework.InlineVolumeTestDriver = &azureFileDriver{} +var _ storageframework.PreprovisionedPVTestDriver = &azureFileDriver{} +var _ storageframework.DynamicPVTestDriver = &azureFileDriver{} + +// InitAzureFileDriver returns azureFileDriver that implements TestDriver interface +func InitAzureFileDriver() storageframework.TestDriver { + return &azureFileDriver{ + driverInfo: storageframework.DriverInfo{ + Name: "azure-file", + InTreePluginName: "kubernetes.io/azure-file", + MaxFileSize: storageframework.FileSizeLarge, + SupportedSizeRange: e2evolume.SizeRange{ + Min: "1Gi", + }, + SupportedFsType: sets.NewString( + "", // Default fsType + ), + Capabilities: map[storageframework.Capability]bool{ + storageframework.CapPersistence: true, + storageframework.CapExec: true, + storageframework.CapRWX: true, + storageframework.CapMultiPODs: true, + storageframework.CapControllerExpansion: true, + storageframework.CapNodeExpansion: true, + }, + }, + } +} + +func (a *azureFileDriver) GetDriverInfo() *storageframework.DriverInfo { + return &a.driverInfo +} + +func (a *azureFileDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) { + e2eskipper.SkipUnlessProviderIs("azure") +} + +func (a *azureFileDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource { + av, ok := e2evolume.(*azureFileVolume) + framework.ExpectEqual(ok, true, "Failed to cast test volume to Azure test volume") + volSource := v1.VolumeSource{ + AzureFile: &v1.AzureFileVolumeSource{ + SecretName: av.secretName, + ShareName: av.shareName, + ReadOnly: readOnly, + }, + } + return &volSource +} + +func (a *azureFileDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) { + av, ok := e2evolume.(*azureFileVolume) + framework.ExpectEqual(ok, true, "Failed to cast test volume to Azure test volume") + pvSource := v1.PersistentVolumeSource{ + AzureFile: &v1.AzureFilePersistentVolumeSource{ + SecretName: av.secretName, + ShareName: av.shareName, + SecretNamespace: nil, + ReadOnly: readOnly, + }, + } + return &pvSource, nil +} + +func (a *azureFileDriver) GetDynamicProvisionStorageClass(config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass { + provisioner := "kubernetes.io/azure-file" + parameters := map[string]string{} + ns := config.Framework.Namespace.Name + immediateBinding := storagev1.VolumeBindingImmediate + return storageframework.GetStorageClass(provisioner, parameters, &immediateBinding, ns) +} + +func (a *azureFileDriver) PrepareTest(f *framework.Framework) (*storageframework.PerTestConfig, func()) { + return &storageframework.PerTestConfig{ + Driver: a, + Prefix: "azure-file", + Framework: f, + }, func() {} +} + +func (a *azureFileDriver) CreateVolume(config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume { + ginkgo.By("creating a test azure file volume") + accountName, shareName, err := e2epv.CreateShare() + framework.ExpectNoError(err) + return &azureFileVolume{ + accountName: accountName, + shareName: shareName, + secretName: "", + } +} + +func (v *azureFileVolume) DeleteVolume() { + err := e2epv.DeleteShare(v.accountName, v.shareName) + framework.ExpectNoError(err) +} diff --git a/test/e2e/storage/in_tree_volumes.go b/test/e2e/storage/in_tree_volumes.go index b5572837d0e..af75f4dafe3 100644 --- a/test/e2e/storage/in_tree_volumes.go +++ b/test/e2e/storage/in_tree_volumes.go @@ -39,6 +39,7 @@ var testDrivers = []func() storageframework.TestDriver{ drivers.InitWindowsGcePdDriver, drivers.InitVSphereDriver, drivers.InitAzureDiskDriver, + drivers.InitAzureFileDriver, drivers.InitAwsDriver, drivers.InitLocalDriverWithVolumeType(utils.LocalVolumeDirectory), drivers.InitLocalDriverWithVolumeType(utils.LocalVolumeDirectoryLink),