e2etest/newe2e_resource_manager_interface.go (207 lines of code) (raw):
package e2etest
import (
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/lease"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/share"
"github.com/Azure/azure-storage-azcopy/v10/cmd"
"github.com/Azure/azure-storage-azcopy/v10/common"
"io"
"time"
)
type GetURIOptions struct {
RemoteOpts RemoteURIOpts
LocalOpts LocalURIOpts
AzureOpts AzureURIOpts
// The wildcard string to append to the end of a resource URI.
Wildcard string
}
type RemoteURIOpts struct {
// Defaults as https
Scheme string
}
type AzureURIOpts struct {
// Must be manually specified
WithSAS bool
// Defaults to a resource-level specific minimally permissioned SAS token.
SASValues GenericSignatureValues
}
type LocalURIOpts struct {
PreferUNCPath bool
}
type ResourceManager interface {
Location() common.Location
Level() cmd.LocationLevel
// URI gets the resource "URI", either a local path or a remote URL.
// This should panic if things fail or make no sense, as a sanity check to the test author.
URI(opts ...GetURIOptions) string
// Parent specifies the parent resource manager, for the purposes of building a tree.
// Can return nil, indicating this is the root of the tree.
Parent() ResourceManager
// Account specifies the parent account.
// Can return nil, indicating there is no associated account
Account() AccountResourceManager
/*Canon specifies an object's canonical location in the resource tree created by a test.
A Canon string is a `/` delimited list of parents up to the final element, representing the resource itself.
The format goes
<account>/<location>/<container>/<object>
For locations where an account does not exist (e.g. local), substitute account with "accountless".
e.g. accountless/Local/<tmpdirname>/<object>
For flat namespaces, e.g. raw blob, / is ignored past objects, as it gets no more granular.
*/
Canon() string
}
type RemoteResourceManager interface {
ResourceManager
// ValidAuthTypes specifies acceptable auth types to use
ValidAuthTypes() ExplicitCredentialTypes
// DefaultAuthType should return the resource's logical default auth type (e.g. SAS)
DefaultAuthType() ExplicitCredentialTypes
// WithSpecificAuthType should return a copy of itself with a specific auth type, intended for RunAzCopy
WithSpecificAuthType(cred ExplicitCredentialTypes, a Asserter, opts ...CreateAzCopyTargetOptions) AzCopyTarget
// ResourceClient attempts to return the native resource client, and should be wrangled by a caller knowing what they're expecting.
ResourceClient() any
}
func TryApplySpecificAuthType(rm ResourceManager, cred ExplicitCredentialTypes, a Asserter, opts ...CreateAzCopyTargetOptions) AzCopyTarget {
if rrm, ok := rm.(RemoteResourceManager); ok {
return rrm.WithSpecificAuthType(cred, a, opts...)
}
return CreateAzCopyTarget(rm, EExplicitCredentialType.None(), a, opts...)
}
// ExplicitCredentialTypes defines a more explicit enum for credential types as AzCopy's internal definition is very loose (e.g. Anonymous can be public or SAS); accepts the URI as-is.
// ExplicitCredentialTypes is a set of bitflags indicating intended credential types for a test and available credential types for resources
type ExplicitCredentialTypes uint8
const EExplicitCredentialType ExplicitCredentialTypes = 0
func (ExplicitCredentialTypes) None() ExplicitCredentialTypes { return 0 } // Used if the goal is to trigger an auth failure
func (ExplicitCredentialTypes) PublicAuth() ExplicitCredentialTypes { return 1 }
func (ExplicitCredentialTypes) SASToken() ExplicitCredentialTypes { return 1 << 1 }
func (ExplicitCredentialTypes) OAuth() ExplicitCredentialTypes { return 1 << 2 }
func (ExplicitCredentialTypes) AcctKey() ExplicitCredentialTypes { return 1 << 3 }
func (ExplicitCredentialTypes) GCP() ExplicitCredentialTypes { return 1 << 4 }
func (ExplicitCredentialTypes) S3() ExplicitCredentialTypes { return 1 << 5 }
func (e ExplicitCredentialTypes) Count() int {
if e == 0 {
return 0
}
out := 0
validTypes := []ExplicitCredentialTypes{ // todo: automate with reflection
EExplicitCredentialType.PublicAuth(),
EExplicitCredentialType.SASToken(),
EExplicitCredentialType.OAuth(),
EExplicitCredentialType.AcctKey(),
EExplicitCredentialType.GCP(),
EExplicitCredentialType.S3(),
}
for _, v := range validTypes {
if e&v == v {
out++
}
}
return out
}
func (e ExplicitCredentialTypes) String() string {
if e == 0 {
return "None"
}
out := "{"
enumStr := []string{
"PublicAuth",
"SASToken",
"OAuth",
"AccountKey",
"GCP",
"S3",
}
for idx, str := range enumStr {
if e.Includes(1 << idx) {
if len(out) > 1 {
out += ","
}
out += str
}
}
out += "}"
return out
}
func (e ExplicitCredentialTypes) Includes(x ExplicitCredentialTypes) bool {
return e&x == x
}
func (e ExplicitCredentialTypes) With(x ...ExplicitCredentialTypes) ExplicitCredentialTypes {
out := e
for _, v := range x {
out |= v
}
return out
}
type PropertiesAvailability uint8
const (
PropertiesAvailabilityNone PropertiesAvailability = iota
PropertiesAvailabilityReadOnly
PropertiesAvailabilityReadWrite
)
/*
Resource managers implement the most generic common expectation of features across services.
If you get more complex, you may want to use GetTypeOrAssert[T] or GetTypeOrZero[T] to wrangle the underlying resource manager,
or wrangle to RemoteResourceManager and call ResourceClient() to pull the actual client.
Check newe2e_resource_managers_*.go for the implementation(s) of resource managers.
*/
// AccountResourceManager manages an account.
type AccountResourceManager interface {
AccountName() string
AccountType() AccountType
AvailableServices() []common.Location
GetService(Asserter, common.Location) ServiceResourceManager
}
type ServiceResourceManager interface {
ResourceManager
RemoteResourceManager
ListContainers(a Asserter) []string
GetContainer(string) ContainerResourceManager
IsHierarchical() bool
}
type ContainerResourceManager interface {
ResourceManager
ContainerName() string
// Create should ignore errors of existing containers. It is expected to attempt to track container creation.
Create(a Asserter, props ContainerProperties)
// GetProperties polls container properties.
GetProperties(a Asserter) ContainerProperties
// Delete should ignore errors of nonexistent containers
Delete(a Asserter)
// ListObjects treats prefixOrDirectory as a prefix when in a non-hierarchical service, and as a directory in a hierarchical service.
// The map will be the real path, relative to container root, not to prefix/directory.
ListObjects(a Asserter, prefixOrDirectory string, recursive bool) map[string]ObjectProperties
// GetObject scales up to a target ObjectResourceManager
GetObject(a Asserter, path string, eType common.EntityType) ObjectResourceManager
// Exists determines if the container in question exists
Exists() bool
}
type ContainerProperties struct {
// Used by multiple services, makes sense to have as a general option.
// When specified by user: Nil = unspecified/unverified
// When returned by manager: Nil = unsupported
Metadata common.Metadata
// BlobContainerProperties is shared with BlobFS, because they're the same resource and share parameters
BlobContainerProperties BlobContainerProperties
FileContainerProperties FileContainerProperties
}
type BlobContainerProperties struct {
Access *container.PublicAccessType
CPKScopeInfo *container.CPKScopeInfo
}
type FileContainerProperties struct {
AccessTier *share.AccessTier
EnabledProtocols *string
Quota *int32
RootSquash *share.RootSquash
}
type ObjectResourceManager interface {
ResourceManager
EntityType() common.EntityType
ContainerName() string
ObjectName() string
// Create attempts to create an object. Should overwrite objects if they already exist.
// It is expected to attempt to track object creation.
// It is also expected to create parents, if required.
Create(a Asserter, body ObjectContentContainer, properties ObjectProperties)
// Delete attempts to delete an object. NotFound type errors are ignored.
Delete(a Asserter)
// ListChildren will fail if EntityType is not a folder and the service is hierarchical.
// The map will be relative to the object.
ListChildren(a Asserter, recursive bool) map[string]ObjectProperties
GetProperties(a Asserter) ObjectProperties
SetHTTPHeaders(a Asserter, h contentHeaders)
SetMetadata(a Asserter, metadata common.Metadata)
SetObjectProperties(a Asserter, props ObjectProperties)
Download(a Asserter) io.ReadSeeker
// Exists determines if the object in question exists
Exists() bool
}
type ObjectProperties struct {
EntityType common.EntityType
HTTPHeaders contentHeaders
Metadata common.Metadata
LastModifiedTime *time.Time
BlobProperties BlobProperties
BlobFSProperties BlobFSProperties
FileProperties FileProperties
}
type BlobProperties struct {
Type *blob.BlobType
Tags map[string]string
BlockBlobAccessTier *blob.AccessTier
PageBlobAccessTier *pageblob.PremiumPageBlobAccessTier
VersionId *string
LeaseState *lease.StateType
LeaseDuration *lease.DurationType
LeaseStatus *lease.StatusType
ArchiveStatus *blob.ArchiveStatus
}
type BlobFSProperties struct {
Permissions *string
Owner *string
Group *string
ACL *string
}
type FileProperties struct {
FileAttributes *string
// ChangeTime, though available on the Files service is not writeable locally, or queryable.
// We also just do not persist it, even on Files at the moment.
// Hence, we do not test ChangeTime.
FileCreationTime *time.Time
FileLastWriteTime *time.Time
FilePermissions *string
LastModifiedTime *time.Time
}
func (f FileProperties) hasCustomTimes() bool {
return f.FileCreationTime != nil || f.FileLastWriteTime != nil
}