Commit bc2c79a4 authored by andyzhangx's avatar andyzhangx

add azure premium file support

update bazel and fix goftm use defaultStorageAccountKind fix test failure update godep license file fix staging godeps issue update staging godeps fix comments, use one API call for file creation
parent 12284c98
...@@ -80,7 +80,7 @@ func newBlobDiskController(common *controllerCommon) (*BlobDiskController, error ...@@ -80,7 +80,7 @@ func newBlobDiskController(common *controllerCommon) (*BlobDiskController, error
// If no storage account is given, search all the storage accounts associated with the resource group and pick one that // If no storage account is given, search all the storage accounts associated with the resource group and pick one that
// fits storage type and location. // fits storage type and location.
func (c *BlobDiskController) CreateVolume(blobName, accountName, accountType, location string, requestGB int) (string, string, int, error) { func (c *BlobDiskController) CreateVolume(blobName, accountName, accountType, location string, requestGB int) (string, string, int, error) {
account, key, err := c.common.cloud.ensureStorageAccount(accountName, accountType, c.common.resourceGroup, location, dedicatedDiskAccountNamePrefix) account, key, err := c.common.cloud.ensureStorageAccount(accountName, accountType, string(defaultStorageAccountKind), c.common.resourceGroup, location, dedicatedDiskAccountNamePrefix)
if err != nil { if err != nil {
return "", "", 0, fmt.Errorf("could not get storage key for storage account %s: %v", accountName, err) return "", "", 0, fmt.Errorf("could not get storage key for storage account %s: %v", accountName, err)
} }
...@@ -491,7 +491,7 @@ func (c *BlobDiskController) createStorageAccount(storageAccountName string, sto ...@@ -491,7 +491,7 @@ func (c *BlobDiskController) createStorageAccount(storageAccountName string, sto
cp := storage.AccountCreateParameters{ cp := storage.AccountCreateParameters{
Sku: &storage.Sku{Name: storageAccountType}, Sku: &storage.Sku{Name: storageAccountType},
// switch to use StorageV2 as it's recommended according to https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options // switch to use StorageV2 as it's recommended according to https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options
Kind: storage.StorageV2, Kind: defaultStorageAccountKind,
Tags: map[string]*string{"created-by": to.StringPtr("azure-dd")}, Tags: map[string]*string{"created-by": to.StringPtr("azure-dd")},
Location: &location} Location: &location}
ctx, cancel := getContextWithCancel() ctx, cancel := getContextWithCancel()
......
...@@ -58,22 +58,11 @@ func (f *azureFileClient) createFileShare(accountName, accountKey, name string, ...@@ -58,22 +58,11 @@ func (f *azureFileClient) createFileShare(accountName, accountKey, name string,
if err != nil { if err != nil {
return err return err
} }
// create a file share and set quota
// Note. Per https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Share,
// setting x-ms-share-quota can set quota on the new share, but in reality, setting quota in CreateShare
// receives error "The metadata specified is invalid. It has characters that are not permitted."
// As a result,breaking into two API calls: create share and set quota
share := fileClient.GetShareReference(name) share := fileClient.GetShareReference(name)
share.Properties.Quota = sizeGiB
if err = share.Create(nil); err != nil { if err = share.Create(nil); err != nil {
return fmt.Errorf("failed to create file share, err: %v", err) return fmt.Errorf("failed to create file share, err: %v", err)
} }
share.Properties.Quota = sizeGiB
if err = share.SetProperties(nil); err != nil {
if err := share.Delete(nil); err != nil {
glog.Errorf("Error deleting share: %v", err)
}
return fmt.Errorf("failed to set quota on file share %s, err: %v", name, err)
}
return nil return nil
} }
......
...@@ -25,18 +25,20 @@ import ( ...@@ -25,18 +25,20 @@ import (
const ( const (
defaultStorageAccountType = string(storage.StandardLRS) defaultStorageAccountType = string(storage.StandardLRS)
defaultStorageAccountKind = storage.StorageV2
fileShareAccountNamePrefix = "f" fileShareAccountNamePrefix = "f"
sharedDiskAccountNamePrefix = "ds" sharedDiskAccountNamePrefix = "ds"
dedicatedDiskAccountNamePrefix = "dd" dedicatedDiskAccountNamePrefix = "dd"
) )
// CreateFileShare creates a file share, using a matching storage account // CreateFileShare creates a file share, using a matching storage account type, account kind, etc.
func (az *Cloud) CreateFileShare(shareName, accountName, accountType, resourceGroup, location string, requestGiB int) (string, string, error) { // storage account will be created if specified account is not found
func (az *Cloud) CreateFileShare(shareName, accountName, accountType, accountKind, resourceGroup, location string, requestGiB int) (string, string, error) {
if resourceGroup == "" { if resourceGroup == "" {
resourceGroup = az.resourceGroup resourceGroup = az.resourceGroup
} }
account, key, err := az.ensureStorageAccount(accountName, accountType, resourceGroup, location, fileShareAccountNamePrefix) account, key, err := az.ensureStorageAccount(accountName, accountType, accountKind, resourceGroup, location, fileShareAccountNamePrefix)
if err != nil { if err != nil {
return "", "", fmt.Errorf("could not get storage key for storage account %s: %v", accountName, err) return "", "", fmt.Errorf("could not get storage key for storage account %s: %v", accountName, err)
} }
......
...@@ -30,6 +30,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -30,6 +30,7 @@ func TestCreateFileShare(t *testing.T) {
name := "baz" name := "baz"
sku := "sku" sku := "sku"
kind := "StorageV2"
location := "centralus" location := "centralus"
value := "foo key" value := "foo key"
bogus := "bogus" bogus := "bogus"
...@@ -38,6 +39,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -38,6 +39,7 @@ func TestCreateFileShare(t *testing.T) {
name string name string
acct string acct string
acctType string acctType string
acctKind string
loc string loc string
gb int gb int
accounts storage.AccountListResult accounts storage.AccountListResult
...@@ -52,6 +54,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -52,6 +54,7 @@ func TestCreateFileShare(t *testing.T) {
name: "foo", name: "foo",
acct: "bar", acct: "bar",
acctType: "type", acctType: "type",
acctKind: "StorageV2",
loc: "eastus", loc: "eastus",
gb: 10, gb: 10,
expectErr: true, expectErr: true,
...@@ -60,6 +63,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -60,6 +63,7 @@ func TestCreateFileShare(t *testing.T) {
name: "foo", name: "foo",
acct: "", acct: "",
acctType: "type", acctType: "type",
acctKind: "StorageV2",
loc: "eastus", loc: "eastus",
gb: 10, gb: 10,
expectErr: true, expectErr: true,
...@@ -68,11 +72,12 @@ func TestCreateFileShare(t *testing.T) { ...@@ -68,11 +72,12 @@ func TestCreateFileShare(t *testing.T) {
name: "foo", name: "foo",
acct: "", acct: "",
acctType: sku, acctType: sku,
acctKind: kind,
loc: location, loc: location,
gb: 10, gb: 10,
accounts: storage.AccountListResult{ accounts: storage.AccountListResult{
Value: &[]storage.Account{ Value: &[]storage.Account{
{Name: &name, Sku: &storage.Sku{Name: storage.SkuName(sku)}, Location: &location}, {Name: &name, Sku: &storage.Sku{Name: storage.SkuName(sku)}, Kind: storage.Kind(kind), Location: &location},
}, },
}, },
keys: storage.AccountListKeysResult{ keys: storage.AccountListKeysResult{
...@@ -87,6 +92,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -87,6 +92,7 @@ func TestCreateFileShare(t *testing.T) {
name: "foo", name: "foo",
acct: "", acct: "",
acctType: sku, acctType: sku,
acctKind: kind,
loc: location, loc: location,
gb: 10, gb: 10,
accounts: storage.AccountListResult{ accounts: storage.AccountListResult{
...@@ -100,6 +106,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -100,6 +106,7 @@ func TestCreateFileShare(t *testing.T) {
name: "foo", name: "foo",
acct: "", acct: "",
acctType: sku, acctType: sku,
acctKind: kind,
loc: location, loc: location,
gb: 10, gb: 10,
accounts: storage.AccountListResult{ accounts: storage.AccountListResult{
...@@ -116,7 +123,7 @@ func TestCreateFileShare(t *testing.T) { ...@@ -116,7 +123,7 @@ func TestCreateFileShare(t *testing.T) {
fake.Keys = test.keys fake.Keys = test.keys
fake.Err = test.err fake.Err = test.err
account, key, err := cloud.CreateFileShare(test.name, test.acct, test.acctType, "rg", test.loc, test.gb) account, key, err := cloud.CreateFileShare(test.name, test.acct, test.acctType, test.acctKind, "rg", test.loc, test.gb)
if test.expectErr && err == nil { if test.expectErr && err == nil {
t.Errorf("unexpected non-error") t.Errorf("unexpected non-error")
continue continue
......
...@@ -30,7 +30,7 @@ type accountWithLocation struct { ...@@ -30,7 +30,7 @@ type accountWithLocation struct {
} }
// getStorageAccounts gets name, type, location of all storage accounts in a resource group which matches matchingAccountType, matchingLocation // getStorageAccounts gets name, type, location of all storage accounts in a resource group which matches matchingAccountType, matchingLocation
func (az *Cloud) getStorageAccounts(matchingAccountType, resourceGroup, matchingLocation string) ([]accountWithLocation, error) { func (az *Cloud) getStorageAccounts(matchingAccountType, matchingAccountKind, resourceGroup, matchingLocation string) ([]accountWithLocation, error) {
ctx, cancel := getContextWithCancel() ctx, cancel := getContextWithCancel()
defer cancel() defer cancel()
result, err := az.StorageAccountClient.ListByResourceGroup(ctx, resourceGroup) result, err := az.StorageAccountClient.ListByResourceGroup(ctx, resourceGroup)
...@@ -49,6 +49,10 @@ func (az *Cloud) getStorageAccounts(matchingAccountType, resourceGroup, matching ...@@ -49,6 +49,10 @@ func (az *Cloud) getStorageAccounts(matchingAccountType, resourceGroup, matching
continue continue
} }
if matchingAccountKind != "" && !strings.EqualFold(matchingAccountKind, string(acct.Kind)) {
continue
}
location := *acct.Location location := *acct.Location
if matchingLocation != "" && !strings.EqualFold(matchingLocation, location) { if matchingLocation != "" && !strings.EqualFold(matchingLocation, location) {
continue continue
...@@ -86,10 +90,10 @@ func (az *Cloud) getStorageAccesskey(account, resourceGroup string) (string, err ...@@ -86,10 +90,10 @@ func (az *Cloud) getStorageAccesskey(account, resourceGroup string) (string, err
} }
// ensureStorageAccount search storage account, create one storage account(with genAccountNamePrefix) if not found, return accountName, accountKey // ensureStorageAccount search storage account, create one storage account(with genAccountNamePrefix) if not found, return accountName, accountKey
func (az *Cloud) ensureStorageAccount(accountName, accountType, resourceGroup, location, genAccountNamePrefix string) (string, string, error) { func (az *Cloud) ensureStorageAccount(accountName, accountType, accountKind, resourceGroup, location, genAccountNamePrefix string) (string, string, error) {
if len(accountName) == 0 { if len(accountName) == 0 {
// find a storage account that matches accountType // find a storage account that matches accountType
accounts, err := az.getStorageAccounts(accountType, resourceGroup, location) accounts, err := az.getStorageAccounts(accountType, accountKind, resourceGroup, location)
if err != nil { if err != nil {
return "", "", fmt.Errorf("could not list storage accounts for account type %s: %v", accountType, err) return "", "", fmt.Errorf("could not list storage accounts for account type %s: %v", accountType, err)
} }
...@@ -109,12 +113,16 @@ func (az *Cloud) ensureStorageAccount(accountName, accountType, resourceGroup, l ...@@ -109,12 +113,16 @@ func (az *Cloud) ensureStorageAccount(accountName, accountType, resourceGroup, l
accountType = defaultStorageAccountType accountType = defaultStorageAccountType
} }
glog.V(2).Infof("azure - no matching account found, begin to create a new account %s in resource group %s, location: %s, accountType: %s", // use StorageV2 by default per https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options
accountName, resourceGroup, location, accountType) kind := defaultStorageAccountKind
if accountKind != "" {
kind = storage.Kind(accountKind)
}
glog.V(2).Infof("azure - no matching account found, begin to create a new account %s in resource group %s, location: %s, accountType: %s, accountKind: %s",
accountName, resourceGroup, location, accountType, kind)
cp := storage.AccountCreateParameters{ cp := storage.AccountCreateParameters{
Sku: &storage.Sku{Name: storage.SkuName(accountType)}, Sku: &storage.Sku{Name: storage.SkuName(accountType)},
// switch to use StorageV2 as it's recommended according to https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options Kind: kind,
Kind: storage.StorageV2,
AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{EnableHTTPSTrafficOnly: to.BoolPtr(true)}, AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{EnableHTTPSTrafficOnly: to.BoolPtr(true)},
Tags: map[string]*string{"created-by": to.StringPtr("azure")}, Tags: map[string]*string{"created-by": to.StringPtr("azure")},
Location: &location} Location: &location}
......
...@@ -27,6 +27,7 @@ go_library( ...@@ -27,6 +27,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library",
"//vendor/github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-07-01/storage:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
], ],
) )
......
...@@ -20,7 +20,9 @@ import ( ...@@ -20,7 +20,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-07-01/storage"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -38,7 +40,7 @@ var _ volume.ProvisionableVolumePlugin = &azureFilePlugin{} ...@@ -38,7 +40,7 @@ var _ volume.ProvisionableVolumePlugin = &azureFilePlugin{}
// azure cloud provider should implement it // azure cloud provider should implement it
type azureCloudProvider interface { type azureCloudProvider interface {
// create a file share // create a file share
CreateFileShare(shareName, accountName, accountType, resourceGroup, location string, requestGiB int) (string, string, error) CreateFileShare(shareName, accountName, accountType, accountKind, resourceGroup, location string, requestGiB int) (string, string, error)
// delete a file share // delete a file share
DeleteFileShare(accountName, accountKey, shareName string) error DeleteFileShare(accountName, accountKey, shareName string) error
// resize a file share // resize a file share
...@@ -171,7 +173,12 @@ func (a *azureFileProvisioner) Provision(selectedNode *v1.Node, allowedTopologie ...@@ -171,7 +173,12 @@ func (a *azureFileProvisioner) Provision(selectedNode *v1.Node, allowedTopologie
return nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Azure file") return nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Azure file")
} }
account, key, err := a.azureProvider.CreateFileShare(name, account, sku, resourceGroup, location, requestGiB) // when use azure file premium, account kind should be specified as FileStorage
accountKind := string(storage.StorageV2)
if strings.HasPrefix(strings.ToLower(sku), "premium") {
accountKind = string(storage.FileStorage)
}
account, key, err := a.azureProvider.CreateFileShare(name, account, sku, accountKind, resourceGroup, location, requestGiB)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -16,23 +16,27 @@ ...@@ -16,23 +16,27 @@
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/autorest", "ImportPath": "github.com/Azure/go-autorest/autorest",
"Rev": "bca49d5b51a50dc5bb17bbf6204c711c6dbded06" "Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/autorest/adal", "ImportPath": "github.com/Azure/go-autorest/autorest/adal",
"Rev": "bca49d5b51a50dc5bb17bbf6204c711c6dbded06" "Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/autorest/azure", "ImportPath": "github.com/Azure/go-autorest/autorest/azure",
"Rev": "bca49d5b51a50dc5bb17bbf6204c711c6dbded06" "Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/autorest/date", "ImportPath": "github.com/Azure/go-autorest/autorest/date",
"Rev": "bca49d5b51a50dc5bb17bbf6204c711c6dbded06" "Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
},
{
"ImportPath": "github.com/Azure/go-autorest/logger",
"Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/version", "ImportPath": "github.com/Azure/go-autorest/version",
"Rev": "bca49d5b51a50dc5bb17bbf6204c711c6dbded06" "Rev": "a88c19ef2016e095f0b6c3b451074b4663f53bed"
}, },
{ {
"ImportPath": "github.com/davecgh/go-spew/spew", "ImportPath": "github.com/davecgh/go-spew/spew",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment