e2etest/factory.go (213 lines of code) (raw):
// Copyright © Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package e2etest
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
blobsas "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
blobservice "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake"
datalakeservice "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/service"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
filesas "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/sas"
fileservice "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/service"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/share"
"github.com/google/uuid"
"os"
"path"
"runtime"
"strings"
"testing"
"time"
)
// provide convenient methods to get access to test resources such as accounts, containers/shares, directories
type TestResourceFactory struct{}
func (TestResourceFactory) GetBlobServiceURL(accountType AccountType) *blobservice.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
var resourceURL string
if accountName != "devstoreaccount1" {
resourceURL = fmt.Sprintf("https://%s.blob.core.windows.net/", accountName)
} else {
resourceURL = fmt.Sprintf("http://127.0.0.1:10000/%s/", accountName)
}
credential, err := blob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
panic(err)
}
bsc, err := blobservice.NewClientWithSharedKeyCredential(resourceURL, credential, nil)
if err != nil {
panic(err)
}
return bsc
}
func (TestResourceFactory) GetFileServiceURL(accountType AccountType) *fileservice.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
resourceURL := fmt.Sprintf("https://%s.file.core.windows.net/", accountName)
credential, err := file.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
panic(err)
}
fsc, err := fileservice.NewClientWithSharedKeyCredential(resourceURL, credential, &fileservice.ClientOptions{AllowTrailingDot: to.Ptr(true)})
if err != nil {
panic(err)
}
return fsc
}
func (TestResourceFactory) GetDatalakeServiceURL(accountType AccountType) *datalakeservice.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
resourceURL := fmt.Sprintf("https://%s.dfs.core.windows.net/", accountName)
credential, err := azdatalake.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
panic(err)
}
dsc, err := datalakeservice.NewClientWithSharedKeyCredential(resourceURL, credential, nil)
if err != nil {
panic(err)
}
return dsc
}
func (TestResourceFactory) GetBlobServiceURLWithSAS(c asserter, accountType AccountType) *blobservice.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
credential, err := blob.NewSharedKeyCredential(accountName, accountKey)
c.AssertNoErr(err)
rawURL := fmt.Sprintf("https://%s.blob.core.windows.net/", credential.AccountName())
client, err := blobservice.NewClientWithSharedKeyCredential(rawURL, credential, nil)
c.AssertNoErr(err)
sasURL, err := client.GetSASURL(
blobsas.AccountResourceTypes{Service: true, Container: true, Object: true},
blobsas.AccountPermissions{Read: true, List: true, Write: true, Delete: true, DeletePreviousVersion: true, Add: true, Create: true, Update: true, Process: true, Tag: true, FilterByTags: true},
time.Now().Add(48*time.Hour),
nil)
c.AssertNoErr(err)
client, err = blobservice.NewClientWithNoCredential(sasURL, nil)
c.AssertNoErr(err)
return client
}
func (TestResourceFactory) GetContainerURLWithSAS(c asserter, accountType AccountType, containerName string) *container.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
credential, err := blob.NewSharedKeyCredential(accountName, accountKey)
c.AssertNoErr(err)
rawURL := fmt.Sprintf("https://%s.blob.core.windows.net/%s", credential.AccountName(), containerName)
if accountName == "devstoreaccount1" {
rawURL = fmt.Sprintf("http://127.0.0.1:10000/%s/%s", credential.AccountName(), containerName)
}
permissions := blobsas.ContainerPermissions{Read: true, Add: true, Write: true, Create: true, Delete: true, DeletePreviousVersion: true, List: true, ModifyOwnership: true, ModifyPermissions: true, Tag: true}
qps, err := blobsas.BlobSignatureValues{
Version: blobsas.Version,
Protocol: blobsas.ProtocolHTTPSandHTTP,
ContainerName: containerName,
Permissions: permissions.String(),
StartTime: time.Time{},
ExpiryTime: time.Now().Add(48 * time.Hour).UTC(),
}.SignWithSharedKey(credential)
c.AssertNoErr(err)
sasURL := rawURL + "?" + qps.Encode()
client, err := container.NewClientWithNoCredential(sasURL, nil)
c.AssertNoErr(err)
return client
}
func (TestResourceFactory) GetFileShareURLWithSAS(c asserter, accountType AccountType, containerName string) *share.Client {
accountName, accountKey := GlobalInputManager{}.GetAccountAndKey(accountType)
credential, err := file.NewSharedKeyCredential(accountName, accountKey)
c.AssertNoErr(err)
rawURL := fmt.Sprintf("https://%s.file.core.windows.net/%s", credential.AccountName(), containerName)
client, err := share.NewClientWithSharedKeyCredential(rawURL, credential, nil)
c.AssertNoErr(err)
sasURL, err := client.GetSASURL(
filesas.SharePermissions{Read: true, Write: true, Create: true, Delete: true, List: true},
time.Now().Add(48*time.Hour),
nil)
c.AssertNoErr(err)
client, err = share.NewClientWithNoCredential(sasURL, &share.ClientOptions{AllowTrailingDot: to.Ptr(true)})
c.AssertNoErr(err)
return client
}
func (TestResourceFactory) GetBlobURLWithSAS(c asserter, accountType AccountType, containerName string, blobName string) *blob.Client {
containerURLWithSAS := TestResourceFactory{}.GetContainerURLWithSAS(c, accountType, containerName)
blobURLWithSAS := containerURLWithSAS.NewBlobClient(blobName)
return blobURLWithSAS
}
func (TestResourceFactory) CreateNewContainer(c asserter, publicAccess *container.PublicAccessType, accountType AccountType) (cc *container.Client, name string, rawURL string) {
name = TestResourceNameGenerator{}.GenerateContainerName(c)
cc = TestResourceFactory{}.GetBlobServiceURL(accountType).NewContainerClient(name)
_, err := cc.Create(context.Background(), &container.CreateOptions{Access: publicAccess})
c.AssertNoErr(err)
return cc, name, TestResourceFactory{}.GetContainerURLWithSAS(c, accountType, name).URL()
}
const defaultShareQuotaGB = int32(512)
func (TestResourceFactory) CreateNewFileShare(c asserter, accountType AccountType) (fileShare *share.Client, name string, rawSasURL string) {
name = TestResourceNameGenerator{}.GenerateContainerName(c)
fileShare = TestResourceFactory{}.GetFileServiceURL(accountType).NewShareClient(name)
_, err := fileShare.Create(context.Background(), &share.CreateOptions{Quota: to.Ptr(defaultShareQuotaGB)})
c.AssertNoErr(err)
return fileShare, name, TestResourceFactory{}.GetFileShareURLWithSAS(c, accountType, name).URL()
}
func (TestResourceFactory) CreateNewFileShareSnapshot(c asserter, fileShare *share.Client) (snapshotID string) {
resp, err := fileShare.CreateSnapshot(context.TODO(), nil)
c.AssertNoErr(err)
return *resp.Snapshot
}
func (TestResourceFactory) CreateLocalDirectory(c asserter) (dstDirName string) {
dstDirName, err := os.MkdirTemp("", "AzCopyLocalTest")
c.AssertNoErr(err)
return
}
type TestResourceNameGenerator struct{}
const (
containerPrefix = "e2e"
blobPrefix = "blob"
)
func getTestName(t *testing.T) (pseudoSuite, test string) {
removeUnderscores := func(s string) string {
return strings.Replace(s, "_", "-", -1) // necessary if using name as basis for blob container name
}
testName := t.Name()
// Look up the stack to find out more info about the test method
// Note: the way to do this changed in go 1.12, refer to release notes for more info
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
fileName := ""
for {
frame, more := frames.Next()
if strings.HasSuffix(frame.Func.Name(), "."+testName) {
fileName = frame.File
break
} else if !more {
break
}
}
// When using the basic Testing package, we have adopted a convention that
// the test name should being with one of the words in the file name, followed by a _ .
// Try to extract a "pseudo suite" name from the test name according to that rule.
pseudoSuite = ""
testName = strings.Replace(testName, "Test", "", 1)
uscorePos := strings.Index(testName, "_")
if uscorePos >= 0 && uscorePos < len(testName)-1 {
beforeUnderscore := strings.ToLower(testName[:uscorePos])
fileWords := strings.ReplaceAll(
strings.TrimSuffix(strings.TrimPrefix(path.Base(fileName), "zt_"), "_test.go"), "_", "")
if strings.Contains(fileWords, beforeUnderscore) {
pseudoSuite = beforeUnderscore
testName = testName[uscorePos+1:]
}
// fileWords := strings.Split(strings.Replace(strings.ToLower(filepath.Base(fileName)), "_test.go", "", -1), "_")
// for _, w := range fileWords {
// if beforeUnderscore == w {
// pseudoSuite = beforeUnderscore
// testName = testName[uscorePos+1:]
// break
// }
// }
}
return pseudoSuite, removeUnderscores(testName)
}
// This function generates an entity name by concatenating the passed prefix,
// the name of the test requesting the entity name, and the minute, second, and nanoseconds of the call.
// This should make it easy to associate the entities with their test, uniquely identify
// them, and determine the order in which they were created.
// Will truncate the end of the test name, if there is not enough room for it, followed by the time-based suffix,
// with a non-zero maxLen.
func generateName(c asserter, prefix string, maxLen int) string {
name := c.CompactScenarioName() // don't want to just use test name here, because each test contains multiple scenarios with the declarative runner
textualPortion := fmt.Sprintf("%s-%s", prefix, strings.ToLower(name))
// GUIDs are less prone to overlap than times.
guidSuffix := uuid.New().String()
if maxLen > 0 {
maxTextLen := maxLen - len(guidSuffix)
if maxTextLen < 1 {
panic("max len too short")
}
if len(textualPortion) > maxTextLen {
textualPortion = textualPortion[:maxTextLen]
}
}
name = textualPortion + guidSuffix
return name
}
func (TestResourceNameGenerator) GenerateContainerName(c asserter) string {
// return generateName(c, containerPrefix, 63)
return uuid.New().String()
}
func (TestResourceNameGenerator) generateBlobName(c asserter) string {
return generateName(c, blobPrefix, 0)
}