mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Merge pull request #56557 from andyzhangx/azurefile-createaccount
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. fix the create azure file pvc failure if there is no storage account in current resource group **What this PR does / why we need it**: When create an azure file PVC, there will be error if there is no storage account in current resource group. With this PR, a storage account will be created if there is no storage account in current resource group. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #56556 **Special notes for your reviewer**: 1. rephrase the code logic of `CreateFileShare` func. ``` if accountName is empty, then find a storage account that matches accountType if no storage account found, then create a new account else we only use user specified storage account create a file share according to found storage account ``` 2. Use func `getStorageAccountName` to get a unique storage account name by UUID, a storage account for azure file would be like `f0b2b0bd40c010112e897fa`. And in next PR, I will use this function to create storage account for azure disk, the storage account for azure disk would be like `d8f3ad8ad92000f1e1e88bd`. **Release note**: ``` fix the create azure file pvc failure if there is no storage account in current resource group ``` /sig azure /assign @rootfs
This commit is contained in:
commit
cfa6774540
@ -56,6 +56,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
|
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||||
|
@ -91,7 +91,7 @@ func (c *BlobDiskController) CreateVolume(name, storageAccount, storageAccountTy
|
|||||||
accounts = append(accounts, accountWithLocation{Name: storageAccount})
|
accounts = append(accounts, accountWithLocation{Name: storageAccount})
|
||||||
} else {
|
} else {
|
||||||
// find a storage account
|
// find a storage account
|
||||||
accounts, err = c.common.cloud.getStorageAccounts()
|
accounts, err = c.common.cloud.getStorageAccounts(storageAccountType, location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: create a storage account and container
|
// TODO: create a storage account and container
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -52,6 +53,8 @@ const (
|
|||||||
|
|
||||||
// nodeLabelRole specifies the role of a node
|
// nodeLabelRole specifies the role of a node
|
||||||
nodeLabelRole = "kubernetes.io/role"
|
nodeLabelRole = "kubernetes.io/role"
|
||||||
|
|
||||||
|
storageAccountNameMaxLength = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNotInVMSet = errors.New("vm is not in the vmset")
|
var errNotInVMSet = errors.New("vm is not in the vmset")
|
||||||
@ -658,3 +661,13 @@ func (as *availabilitySet) EnsureBackendPoolDeleted(poolID, vmSetName string) er
|
|||||||
// Do nothing for availability set.
|
// Do nothing for availability set.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get a storage account by UUID
|
||||||
|
func generateStorageAccountName(accountNamePrefix string) string {
|
||||||
|
uniqueID := strings.Replace(string(uuid.NewUUID()), "-", "", -1)
|
||||||
|
accountName := strings.ToLower(accountNamePrefix + uniqueID)
|
||||||
|
if len(accountName) > storageAccountNameMaxLength {
|
||||||
|
return accountName[:storageAccountNameMaxLength-1]
|
||||||
|
}
|
||||||
|
return accountName
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 The Kubernetes Authors.
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -90,3 +90,33 @@ func TestGetLastSegment(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateStorageAccountName(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
prefix string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
prefix: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefix: "pvc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefix: "1234512345123451234512345",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
accountName := generateStorageAccountName(test.prefix)
|
||||||
|
if len(accountName) > storageAccountNameMaxLength || len(accountName) < 3 {
|
||||||
|
t.Errorf("input prefix: %s, output account name: %s, length not in [3,%d]", test.prefix, accountName, storageAccountNameMaxLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, char := range accountName {
|
||||||
|
if (char < 'a' || char > 'z') && (char < '0' || char > '9') {
|
||||||
|
t.Errorf("input prefix: %s, output account name: %s, there is non-digit or non-letter(%q)", test.prefix, accountName, char)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,54 +19,75 @@ package azure
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/arm/storage"
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultStorageAccountType = string(storage.StandardLRS)
|
||||||
|
fileShareAccountNamePrefix = "f"
|
||||||
|
)
|
||||||
|
|
||||||
// CreateFileShare creates a file share, using a matching storage account
|
// CreateFileShare creates a file share, using a matching storage account
|
||||||
func (az *Cloud) CreateFileShare(name, storageAccount, storageType, location string, requestGiB int) (string, string, error) {
|
func (az *Cloud) CreateFileShare(shareName, accountName, accountType, location string, requestGiB int) (string, string, error) {
|
||||||
var errResult error
|
if len(accountName) == 0 {
|
||||||
accounts := []accountWithLocation{}
|
// find a storage account that matches accountType
|
||||||
if len(storageAccount) > 0 {
|
accounts, err := az.getStorageAccounts(accountType, location)
|
||||||
accounts = append(accounts, accountWithLocation{Name: storageAccount})
|
if err != nil {
|
||||||
} else {
|
return "", "", fmt.Errorf("could not list storage accounts for account type %s: %v", accountType, err)
|
||||||
// find a storage account
|
}
|
||||||
accounts, errResult = az.getStorageAccounts()
|
|
||||||
if errResult != nil {
|
if len(accounts) > 0 {
|
||||||
// TODO: create a storage account and container
|
accountName = accounts[0].Name
|
||||||
return "", "", errResult
|
glog.V(4).Infof("found a matching account %s type %s location %s", accounts[0].Name, accounts[0].StorageType, accounts[0].Location)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(accountName) == 0 {
|
||||||
|
// not found a matching account, now create a new account in current resource group
|
||||||
|
accountName = generateStorageAccountName(fileShareAccountNamePrefix)
|
||||||
|
if location == "" {
|
||||||
|
location = az.Location
|
||||||
|
}
|
||||||
|
if accountType == "" {
|
||||||
|
accountType = defaultStorageAccountType
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(2).Infof("azureFile - no matching account found, begin to create a new account %s in resource group %s, location: %s, accountType: %s",
|
||||||
|
accountName, az.ResourceGroup, location, accountType)
|
||||||
|
cp := storage.AccountCreateParameters{
|
||||||
|
Sku: &storage.Sku{Name: storage.SkuName(accountType)},
|
||||||
|
Tags: &map[string]*string{"created-by": to.StringPtr("azure-file")},
|
||||||
|
Location: &location}
|
||||||
|
cancel := make(chan struct{})
|
||||||
|
|
||||||
|
_, errchan := az.StorageAccountClient.Create(az.ResourceGroup, accountName, cp, cancel)
|
||||||
|
err := <-errchan
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(fmt.Sprintf("Failed to create storage account %s, error: %s", accountName, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, account := range accounts {
|
}
|
||||||
glog.V(4).Infof("account %s type %s location %s", account.Name, account.StorageType, account.Location)
|
|
||||||
if ((storageType == "" || account.StorageType == storageType) && (location == "" || account.Location == location)) || len(storageAccount) > 0 {
|
|
||||||
// find the access key with this account
|
// find the access key with this account
|
||||||
key, innerErr := az.getStorageAccesskey(account.Name)
|
accountKey, err := az.getStorageAccesskey(accountName)
|
||||||
if innerErr != nil {
|
if err != nil {
|
||||||
errResult = fmt.Errorf("could not get storage key for storage account %s: %v", account.Name, innerErr)
|
return "", "", fmt.Errorf("could not get storage key for storage account %s: %v", accountName, err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if innerErr = az.createFileShare(account.Name, key, name, requestGiB); innerErr != nil {
|
if err := az.createFileShare(accountName, accountKey, shareName, requestGiB); err != nil {
|
||||||
errResult = fmt.Errorf("failed to create share %s in account %s: %v", name, account.Name, innerErr)
|
return "", "", fmt.Errorf("failed to create share %s in account %s: %v", shareName, accountName, err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
glog.V(4).Infof("created share %s in account %s", name, account.Name)
|
glog.V(4).Infof("created share %s in account %s", shareName, accountName)
|
||||||
return account.Name, key, nil
|
return accountName, accountKey, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errResult == nil {
|
|
||||||
errResult = fmt.Errorf("failed to find a matching storage account")
|
|
||||||
}
|
|
||||||
return "", "", errResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFileShare deletes a file share using storage account name and key
|
// DeleteFileShare deletes a file share using storage account name and key
|
||||||
func (az *Cloud) DeleteFileShare(accountName, key, name string) error {
|
func (az *Cloud) DeleteFileShare(accountName, accountKey, shareName string) error {
|
||||||
if err := az.deleteFileShare(accountName, key, name); err != nil {
|
if err := az.deleteFileShare(accountName, accountKey, shareName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.V(4).Infof("share %s deleted", name)
|
glog.V(4).Infof("share %s deleted", shareName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,29 +25,29 @@ type accountWithLocation struct {
|
|||||||
Name, StorageType, Location string
|
Name, StorageType, Location string
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStorageAccounts gets the storage accounts' name, type, location in a resource group
|
// getStorageAccounts gets name, type, location of all storage accounts in a resource group which matches matchingAccountType
|
||||||
func (az *Cloud) getStorageAccounts() ([]accountWithLocation, error) {
|
func (az *Cloud) getStorageAccounts(matchingAccountType, matchingLocation string) ([]accountWithLocation, error) {
|
||||||
result, err := az.StorageAccountClient.ListByResourceGroup(az.ResourceGroup)
|
result, err := az.StorageAccountClient.ListByResourceGroup(az.ResourceGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if result.Value == nil {
|
if result.Value == nil {
|
||||||
return nil, fmt.Errorf("no storage accounts from resource group %s", az.ResourceGroup)
|
return nil, fmt.Errorf("unexpected error when listing storage accounts from resource group %s", az.ResourceGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts := []accountWithLocation{}
|
accounts := []accountWithLocation{}
|
||||||
for _, acct := range *result.Value {
|
for _, acct := range *result.Value {
|
||||||
if acct.Name != nil {
|
if acct.Name != nil && acct.Location != nil && acct.Sku != nil {
|
||||||
name := *acct.Name
|
storageType := string((*acct.Sku).Name)
|
||||||
loc := ""
|
if matchingAccountType != "" && !strings.EqualFold(matchingAccountType, storageType) {
|
||||||
if acct.Location != nil {
|
continue
|
||||||
loc = *acct.Location
|
|
||||||
}
|
}
|
||||||
storageType := ""
|
|
||||||
if acct.Sku != nil {
|
location := *acct.Location
|
||||||
storageType = string((*acct.Sku).Name)
|
if matchingLocation != "" && !strings.EqualFold(matchingLocation, location) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
accounts = append(accounts, accountWithLocation{Name: name, StorageType: storageType, Location: loc})
|
accounts = append(accounts, accountWithLocation{Name: *acct.Name, StorageType: storageType, Location: location})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ 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(name, storageAccount, storageType, location string, requestGiB int) (string, string, error)
|
CreateFileShare(shareName, accountName, accountType, location string, requestGiB int) (string, string, error)
|
||||||
// delete a file share
|
// delete a file share
|
||||||
DeleteFileShare(accountName, key, name string) error
|
DeleteFileShare(accountName, accountKey, shareName string) error
|
||||||
// resize a file share
|
// resize a file share
|
||||||
ResizeFileShare(accountName, accountKey, name string, sizeGiB int) error
|
ResizeFileShare(accountName, accountKey, name string, sizeGiB int) error
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user