diff --git a/sdk/storage/azfile/assets.json b/sdk/storage/azfile/assets.json index 6561d915b461..23448376d5cc 100644 --- a/sdk/storage/azfile/assets.json +++ b/sdk/storage/azfile/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/storage/azfile", - "Tag": "go/storage/azfile_33b8efd383" + "Tag": "go/storage/azfile_5b5e44362e" } diff --git a/sdk/storage/azfile/directory/client_test.go b/sdk/storage/azfile/directory/client_test.go index bdd6b71cfb29..2fed9c43c119 100644 --- a/sdk/storage/azfile/directory/client_test.go +++ b/sdk/storage/azfile/directory/client_test.go @@ -60,12 +60,12 @@ type DirectoryUnrecordedTestsSuite struct { suite.Suite } -func (d *DirectoryUnrecordedTestsSuite) TestDirNewDirectoryClient() { +func (d *DirectoryRecordedTestsSuite) TestDirNewDirectoryClient() { _require := require.New(d.T()) testName := d.T().Name() - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -83,12 +83,12 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirNewDirectoryClient() { _require.Equal(subDirClient.URL(), correctURL) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateFileURL() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateFileURL() { _require := require.New(d.T()) testName := d.T().Name() - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -106,7 +106,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateFileURL() { _require.Equal(fileClient.URL(), correctURL) } -func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { +func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { _require := require.New(d.T()) testName := d.T().Name() @@ -122,7 +122,10 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { dirName := testcommon.GenerateDirectoryName(testName) dirURL := "https://" + cred.AccountName() + ".file.core.windows.net/" + shareName + "/" + dirName - dirClient, err := directory.NewClientWithSharedKeyCredential(dirURL, cred, nil) + + options := &directory.ClientOptions{} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClientWithSharedKeyCredential(dirURL, cred, options) _require.NoError(err) resp, err := dirClient.Create(context.Background(), nil) @@ -135,7 +138,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { _require.Equal(resp.FileChangeTime.IsZero(), false) } -func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingConnectionString() { +func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingConnectionString() { _require := require.New(d.T()) testName := d.T().Name() @@ -150,7 +153,9 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingConnectionString defer testcommon.DeleteShare(context.Background(), _require, shareClient) dirName := testcommon.GenerateDirectoryName(testName) - dirClient, err := directory.NewClientFromConnectionString(*connString, shareName, dirName, nil) + options := &directory.ClientOptions{} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClientFromConnectionString(*connString, shareName, dirName, options) _require.NoError(err) resp, err := dirClient.Create(context.Background(), nil) @@ -164,7 +169,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingConnectionString innerDirName1 := "innerdir1" dirPath := dirName + "/" + innerDirName1 - dirClient1, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, nil) + dirClient1, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, options) _require.NoError(err) resp, err = dirClient1.Create(context.Background(), nil) @@ -176,7 +181,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingConnectionString innerDirName2 := "innerdir2" // using '\' as path separator between directories dirPath = dirName + "\\" + innerDirName1 + "\\" + innerDirName2 - dirClient2, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, nil) + dirClient2, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, options) _require.NoError(err) resp, err = dirClient2.Create(context.Background(), nil) @@ -186,7 +191,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateUsingConnectionString _require.Equal(resp.FileCreationTime.IsZero(), false) } -func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateNegativeMultiLevel() { +func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateNegativeMultiLevel() { _require := require.New(d.T()) testName := d.T().Name() @@ -203,7 +208,9 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryCreateNegativeMultiLevel() dirName := testcommon.GenerateDirectoryName(testName) // dirPath where parent dir does not exist dirPath := "a/b/c/d/" + dirName - dirClient, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, nil) + options := &directory.ClientOptions{} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClientFromConnectionString(*connString, shareName, dirPath, options) _require.NoError(err) resp, err := dirClient.Create(context.Background(), nil) @@ -223,11 +230,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryClientUsingSAS() { shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - dirName := testcommon.GenerateDirectoryName(testName) - dirClient := shareClient.NewDirectoryClient(dirName) - - _, err = dirClient.Create(context.Background(), nil) - _require.NoError(err) + dirClient := testcommon.CreateNewDirectory(context.Background(), _require, testcommon.GenerateDirectoryName(testName), shareClient) permissions := sas.FilePermissions{ Read: true, @@ -247,10 +250,33 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirectoryClientUsingSAS() { _require.Error(err) testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthenticationFailed) - // TODO: create files using dirSASClient + subDirSASClient := dirSASClient.NewSubdirectoryClient("subdir") + _, err = subDirSASClient.Create(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthenticationFailed) + + // TODO: directory SAS client unable to do create and get properties on directories. + // Also unable to do create or get properties on files. Validate this behaviour. + fileSASClient := dirSASClient.NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fileSASClient.Create(context.Background(), 1024, nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthenticationFailed) + + _, err = fileSASClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthenticationFailed) + + // create file using shared key client + _, err = dirClient.NewFileClient(testcommon.GenerateFileName(testName)).Create(context.Background(), 1024, nil) + _require.NoError(err) + + // get properties using SAS client + _, err = fileSASClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthenticationFailed) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateDeleteDefault() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateDeleteDefault() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -395,7 +421,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateDeleteNonDefault() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateNegativePermissions() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateNegativePermissions() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -426,7 +452,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateNegativePermissions() { _require.Error(err) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateNegativeAttributes() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateNegativeAttributes() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -448,7 +474,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateNegativeAttributes() { testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidHeaderValue) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateDeleteNegativeMultiLevelDir() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateDeleteNegativeMultiLevelDir() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -491,7 +517,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateDeleteNegativeMultiLevelDir _require.NoError(err) } -func (d *DirectoryUnrecordedTestsSuite) TestDirCreateEndWithSlash() { +func (d *DirectoryRecordedTestsSuite) TestDirCreateEndWithSlash() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -516,7 +542,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirCreateEndWithSlash() { _require.NoError(err) } -func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataDefault() { +func (d *DirectoryRecordedTestsSuite) TestDirGetSetMetadataDefault() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -549,7 +575,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataDefault() { _require.Len(gResp.Metadata, 0) } -func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataNonDefault() { +func (d *DirectoryRecordedTestsSuite) TestDirGetSetMetadataNonDefault() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -589,7 +615,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataNonDefault() { _require.EqualValues(gResp.Metadata, md) } -func (d *DirectoryUnrecordedTestsSuite) TestDirSetMetadataNegative() { +func (d *DirectoryRecordedTestsSuite) TestDirSetMetadataNegative() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -613,7 +639,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirSetMetadataNegative() { _require.Error(err) } -func (d *DirectoryUnrecordedTestsSuite) TestDirGetPropertiesNegative() { +func (d *DirectoryRecordedTestsSuite) TestDirGetPropertiesNegative() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -631,7 +657,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirGetPropertiesNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) } -func (d *DirectoryUnrecordedTestsSuite) TestDirGetPropertiesWithBaseDirectory() { +func (d *DirectoryRecordedTestsSuite) TestDirGetPropertiesWithBaseDirectory() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -656,7 +682,7 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirGetPropertiesWithBaseDirectory() _require.NotNil(gResp.IsServerEncrypted) } -func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataMergeAndReplace() { +func (d *DirectoryRecordedTestsSuite) TestDirGetSetMetadataMergeAndReplace() { _require := require.New(d.T()) testName := d.T().Name() svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -713,10 +739,10 @@ func (d *DirectoryUnrecordedTestsSuite) TestDirGetSetMetadataMergeAndReplace() { _require.EqualValues(gResp.Metadata, md2) } -func (d *DirectoryUnrecordedTestsSuite) TestSASDirectoryClientNoKey() { +func (d *DirectoryRecordedTestsSuite) TestSASDirectoryClientNoKey() { _require := require.New(d.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) testName := d.T().Name() shareName := testcommon.GenerateShareName(testName) @@ -736,12 +762,11 @@ func (d *DirectoryUnrecordedTestsSuite) TestSASDirectoryClientNoKey() { _require.Equal(err, fileerror.MissingSharedKeyCredential) } -func (d *DirectoryUnrecordedTestsSuite) TestSASDirectoryClientSignNegative() { +func (d *DirectoryRecordedTestsSuite) TestSASDirectoryClientSignNegative() { _require := require.New(d.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) - accountKey, err := testcommon.GetRequiredEnv(testcommon.AccountKeyEnvVar) - _require.NoError(err) + accountName, accountKey := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + _require.Greater(len(accountKey), 0) cred, err := service.NewSharedKeyCredential(accountName, accountKey) _require.NoError(err) diff --git a/sdk/storage/azfile/directory/models.go b/sdk/storage/azfile/directory/models.go index 82ccf027f487..7aac2ba2587c 100644 --- a/sdk/storage/azfile/directory/models.go +++ b/sdk/storage/azfile/directory/models.go @@ -24,7 +24,6 @@ type SharedKeyCredential = exported.SharedKeyCredential // CreateOptions contains the optional parameters for the Client.Create method. type CreateOptions struct { // The default value is 'Directory' for Attributes and 'now' for CreationTime and LastWriteTime fields in file.SMBProperties. - // TODO: Change the types of creation time and last write time to string from time.Time to include values like 'now', 'preserve', etc. FileSMBProperties *file.SMBProperties // The default value is 'inherit' for Permission field in file.Permissions. FilePermissions *file.Permissions @@ -86,7 +85,6 @@ func (o *GetPropertiesOptions) format() *generated.DirectoryClientGetPropertiesO // SetPropertiesOptions contains the optional parameters for the Client.SetProperties method. type SetPropertiesOptions struct { // The default value is 'preserve' for Attributes, CreationTime and LastWriteTime fields in file.SMBProperties. - // TODO: Change the types of creation time and last write time to string from time.Time to include values like 'now', 'preserve', etc. FileSMBProperties *file.SMBProperties // The default value is 'preserve' for Permission field in file.Permissions. FilePermissions *file.Permissions diff --git a/sdk/storage/azfile/file/client.go b/sdk/storage/azfile/file/client.go index 27587ad4f455..8546bb5a5763 100644 --- a/sdk/storage/azfile/file/client.go +++ b/sdk/storage/azfile/file/client.go @@ -9,10 +9,17 @@ package file import ( "context" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/fileerror" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/base" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/generated" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/shared" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/sas" "io" "os" + "strings" + "time" ) // ClientOptions contains the optional parameters when creating a Client. @@ -27,16 +34,28 @@ type Client base.Client[generated.FileClient] // This is used to anonymously access a file or with a shared access signature (SAS) token. // - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file? // - options - client options; pass nil to accept the default values +// +// The directoryPath is optional in the fileURL. If omitted, it points to file within the specified share. func NewClientWithNoCredential(fileURL string, options *ClientOptions) (*Client, error) { - return nil, nil + conOptions := shared.GetClientOptions(options) + pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions) + + return (*Client)(base.NewFileClient(fileURL, pl, nil)), nil } // NewClientWithSharedKeyCredential creates an instance of Client with the specified values. // - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file // - cred - a SharedKeyCredential created with the matching file's storage account and access key // - options - client options; pass nil to accept the default values +// +// The directoryPath is optional in the fileURL. If omitted, it points to file within the specified share. func NewClientWithSharedKeyCredential(fileURL string, cred *SharedKeyCredential, options *ClientOptions) (*Client, error) { - return nil, nil + authPolicy := exported.NewSharedKeyCredPolicy(cred) + conOptions := shared.GetClientOptions(options) + conOptions.PerRetryPolicies = append(conOptions.PerRetryPolicies, authPolicy) + pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions) + + return (*Client)(base.NewFileClient(fileURL, pl, cred)), nil } // NewClientFromConnectionString creates an instance of Client with the specified values. @@ -45,7 +64,23 @@ func NewClientWithSharedKeyCredential(fileURL string, cred *SharedKeyCredential, // - filePath - the path of the file within the share // - options - client options; pass nil to accept the default values func NewClientFromConnectionString(connectionString string, shareName string, filePath string, options *ClientOptions) (*Client, error) { - return nil, nil + parsed, err := shared.ParseConnectionString(connectionString) + if err != nil { + return nil, err + } + + filePath = strings.ReplaceAll(filePath, "\\", "/") + parsed.ServiceURL = runtime.JoinPaths(parsed.ServiceURL, shareName, filePath) + + if parsed.AccountKey != "" && parsed.AccountName != "" { + credential, err := exported.NewSharedKeyCredential(parsed.AccountName, parsed.AccountKey) + if err != nil { + return nil, err + } + return NewClientWithSharedKeyCredential(parsed.ServiceURL, credential, options) + } + + return NewClientWithNoCredential(parsed.ServiceURL, options) } func (f *Client) generated() *generated.FileClient { @@ -62,35 +97,45 @@ func (f *Client) URL() string { } // Create operation creates a new file or replaces a file. Note it only initializes the file with no content. -// - fileContentLength: Specifies the maximum size for the file, up to 4 TB. +// - fileContentLength: Specifies the maximum size for the file in bytes, up to 4 TB. // // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/create-file. func (f *Client) Create(ctx context.Context, fileContentLength int64, options *CreateOptions) (CreateResponse, error) { - return CreateResponse{}, nil + fileAttributes, fileCreationTime, fileLastWriteTime, fileCreateOptions, fileHTTPHeaders, leaseAccessConditions := options.format() + resp, err := f.generated().Create(ctx, fileContentLength, fileAttributes, fileCreationTime, fileLastWriteTime, fileCreateOptions, fileHTTPHeaders, leaseAccessConditions) + return resp, err } // Delete operation removes the file from the storage account. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/delete-file2. func (f *Client) Delete(ctx context.Context, options *DeleteOptions) (DeleteResponse, error) { - return DeleteResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().Delete(ctx, opts, leaseAccessConditions) + return resp, err } // GetProperties operation returns all user-defined metadata, standard HTTP properties, and system properties for the file. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/get-file-properties. func (f *Client) GetProperties(ctx context.Context, options *GetPropertiesOptions) (GetPropertiesResponse, error) { - return GetPropertiesResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().GetProperties(ctx, opts, leaseAccessConditions) + return resp, err } // SetHTTPHeaders operation sets HTTP headers on the file. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/set-file-properties. func (f *Client) SetHTTPHeaders(ctx context.Context, options *SetHTTPHeadersOptions) (SetHTTPHeadersResponse, error) { - return SetHTTPHeadersResponse{}, nil + fileAttributes, fileCreationTime, fileLastWriteTime, opts, fileHTTPHeaders, leaseAccessConditions := options.format() + resp, err := f.generated().SetHTTPHeaders(ctx, fileAttributes, fileCreationTime, fileLastWriteTime, opts, fileHTTPHeaders, leaseAccessConditions) + return resp, err } // SetMetadata operation sets user-defined metadata for the specified file. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/set-file-metadata. func (f *Client) SetMetadata(ctx context.Context, options *SetMetadataOptions) (SetMetadataResponse, error) { - return SetMetadataResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().SetMetadata(ctx, opts, leaseAccessConditions) + return resp, err } // StartCopyFromURL operation copies the data at the source URL to a file. @@ -98,7 +143,9 @@ func (f *Client) SetMetadata(ctx context.Context, options *SetMetadataOptions) ( // // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/copy-file. func (f *Client) StartCopyFromURL(ctx context.Context, copySource string, options *StartCopyFromURLOptions) (StartCopyFromURLResponse, error) { - return StartCopyFromURLResponse{}, nil + opts, copyFileSmbInfo, leaseAccessConditions := options.format() + resp, err := f.generated().StartCopy(ctx, copySource, opts, copyFileSmbInfo, leaseAccessConditions) + return resp, err } // AbortCopy operation cancels a pending Copy File operation, and leaves a destination file with zero length and full metadata. @@ -106,7 +153,9 @@ func (f *Client) StartCopyFromURL(ctx context.Context, copySource string, option // // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/abort-copy-file. func (f *Client) AbortCopy(ctx context.Context, copyID string, options *AbortCopyOptions) (AbortCopyResponse, error) { - return AbortCopyResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().AbortCopy(ctx, copyID, opts, leaseAccessConditions) + return resp, err } // DownloadStream operation reads or downloads a file from the system, including its metadata and properties. @@ -129,7 +178,9 @@ func (f *Client) DownloadFile(ctx context.Context, file *os.File, o *DownloadFil // Resize operation resizes the file to the specified size. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/set-file-properties. func (f *Client) Resize(ctx context.Context, size int64, options *ResizeOptions) (ResizeResponse, error) { - return ResizeResponse{}, nil + fileAttributes, fileCreationTime, fileLastWriteTime, opts, leaseAccessConditions := options.format(size) + resp, err := f.generated().SetHTTPHeaders(ctx, fileAttributes, fileCreationTime, fileLastWriteTime, opts, nil, leaseAccessConditions) + return resp, err } // UploadRange operation uploads a range of bytes to a file. @@ -162,5 +213,56 @@ func (f *Client) UploadRangeFromURL(ctx context.Context, copySource string, dest // GetRangeList operation returns the list of valid ranges for a file. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/list-ranges. func (f *Client) GetRangeList(ctx context.Context, options *GetRangeListOptions) (GetRangeListResponse, error) { - return GetRangeListResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().GetRangeList(ctx, opts, leaseAccessConditions) + return resp, err +} + +// ForceCloseHandles operation closes a handle or handles opened on a file. +// - handleID - Specifies the handle ID to be closed. Use an asterisk (*) as a wildcard string to specify all handles. +// +// For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/force-close-handles. +func (f *Client) ForceCloseHandles(ctx context.Context, handleID string, options *ForceCloseHandlesOptions) (ForceCloseHandlesResponse, error) { + opts := options.format() + resp, err := f.generated().ForceCloseHandles(ctx, handleID, opts) + return resp, err +} + +// ListHandles operation returns a list of open handles on a file. +// For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/list-handles. +func (f *Client) ListHandles(ctx context.Context, options *ListHandlesOptions) (ListHandlesResponse, error) { + opts := options.format() + resp, err := f.generated().ListHandles(ctx, opts) + return resp, err +} + +// GetSASURL is a convenience method for generating a SAS token for the currently pointed at file. +// It can only be used if the credential supplied during creation was a SharedKeyCredential. +func (f *Client) GetSASURL(permissions sas.FilePermissions, expiry time.Time, o *GetSASURLOptions) (string, error) { + if f.sharedKey() == nil { + return "", fileerror.MissingSharedKeyCredential + } + st := o.format() + + urlParts, err := sas.ParseURL(f.URL()) + if err != nil { + return "", err + } + + qps, err := sas.SignatureValues{ + Version: sas.Version, + Protocol: sas.ProtocolHTTPS, + ShareName: urlParts.ShareName, + DirectoryOrFilePath: urlParts.DirectoryOrFilePath, + Permissions: permissions.String(), + StartTime: st, + ExpiryTime: expiry.UTC(), + }.SignWithSharedKey(f.sharedKey()) + if err != nil { + return "", err + } + + endpoint := f.URL() + "?" + qps.Encode() + + return endpoint, nil } diff --git a/sdk/storage/azfile/file/client_test.go b/sdk/storage/azfile/file/client_test.go new file mode 100644 index 000000000000..b50b90babc2e --- /dev/null +++ b/sdk/storage/azfile/file/client_test.go @@ -0,0 +1,1061 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package file_test + +import ( + "context" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/fileerror" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/testcommon" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/sas" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/service" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/share" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "strings" + "testing" + "time" +) + +func Test(t *testing.T) { + recordMode := recording.GetRecordMode() + t.Logf("Running file Tests in %s mode\n", recordMode) + if recordMode == recording.LiveMode { + suite.Run(t, &FileRecordedTestsSuite{}) + suite.Run(t, &FileUnrecordedTestsSuite{}) + } else if recordMode == recording.PlaybackMode { + suite.Run(t, &FileRecordedTestsSuite{}) + } else if recordMode == recording.RecordingMode { + suite.Run(t, &FileRecordedTestsSuite{}) + } +} + +func (f *FileRecordedTestsSuite) BeforeTest(suite string, test string) { + testcommon.BeforeTest(f.T(), suite, test) +} + +func (f *FileRecordedTestsSuite) AfterTest(suite string, test string) { + testcommon.AfterTest(f.T(), suite, test) +} + +func (f *FileUnrecordedTestsSuite) BeforeTest(suite string, test string) { + +} + +func (f *FileUnrecordedTestsSuite) AfterTest(suite string, test string) { + +} + +type FileRecordedTestsSuite struct { + suite.Suite +} + +type FileUnrecordedTestsSuite struct { + suite.Suite +} + +func (f *FileRecordedTestsSuite) TestFileNewFileClient() { + _require := require.New(f.T()) + testName := f.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := svcClient.NewShareClient(shareName) + + dirName := testcommon.GenerateDirectoryName(testName) + dirClient := shareClient.NewDirectoryClient(dirName) + + fileName := testcommon.GenerateFileName(testName) + fileClient := dirClient.NewFileClient(fileName) + + correctURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + "/" + fileName + _require.Equal(fileClient.URL(), correctURL) + + rootFileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + + correctURL = "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + fileName + _require.Equal(rootFileClient.URL(), correctURL) +} + +func (f *FileRecordedTestsSuite) TestFileCreateUsingSharedKey() { + _require := require.New(f.T()) + testName := f.T().Name() + + cred, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + fileName := testcommon.GenerateFileName(testName) + fileURL := "https://" + cred.AccountName() + ".file.core.windows.net/" + shareName + "/" + dirName + "/" + fileName + + options := &file.ClientOptions{} + testcommon.SetClientOptions(f.T(), &options.ClientOptions) + fileClient, err := file.NewClientWithSharedKeyCredential(fileURL, cred, options) + _require.NoError(err) + + // creating file where directory does not exist gives ParentNotFound error + _, err = fileClient.Create(context.Background(), 1024, nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ParentNotFound) + + testcommon.CreateNewDirectory(context.Background(), _require, dirName, shareClient) + + resp, err := fileClient.Create(context.Background(), 1024, nil) + _require.NoError(err) + _require.NotNil(resp.ETag) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) + _require.Equal(resp.FileLastWriteTime.IsZero(), false) + _require.Equal(resp.FileChangeTime.IsZero(), false) +} + +func (f *FileRecordedTestsSuite) TestFileCreateUsingConnectionString() { + _require := require.New(f.T()) + testName := f.T().Name() + + connString, err := testcommon.GetGenericConnectionString(testcommon.TestAccountDefault) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + fileName := testcommon.GenerateFileName(testName) + options := &file.ClientOptions{} + testcommon.SetClientOptions(f.T(), &options.ClientOptions) + fileClient1, err := file.NewClientFromConnectionString(*connString, shareName, fileName, options) + _require.NoError(err) + + resp, err := fileClient1.Create(context.Background(), 1024, nil) + _require.NoError(err) + _require.NotNil(resp.ETag) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) + _require.Equal(resp.FileLastWriteTime.IsZero(), false) + _require.Equal(resp.FileChangeTime.IsZero(), false) + + filePath := dirName + "/" + fileName + fileClient2, err := file.NewClientFromConnectionString(*connString, shareName, filePath, options) + _require.NoError(err) + + _, err = fileClient2.Create(context.Background(), 1024, nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ParentNotFound) + + testcommon.CreateNewDirectory(context.Background(), _require, dirName, shareClient) + + // using '\' as path separator + filePath = dirName + "\\" + fileName + fileClient3, err := file.NewClientFromConnectionString(*connString, shareName, filePath, options) + _require.NoError(err) + + resp, err = fileClient3.Create(context.Background(), 1024, nil) + _require.NoError(err) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) +} + +func (f *FileUnrecordedTestsSuite) TestFileClientUsingSAS() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirClient := testcommon.CreateNewDirectory(context.Background(), _require, dirName, shareClient) + + fileName := testcommon.GenerateFileName(testName) + fileClient := dirClient.NewFileClient(fileName) + + permissions := sas.FilePermissions{ + Read: true, + Write: true, + Delete: true, + Create: true, + } + expiry := time.Now().Add(time.Hour) + + fileSASURL, err := fileClient.GetSASURL(permissions, expiry, nil) + _require.NoError(err) + + fileSASClient, err := file.NewClientWithNoCredential(fileSASURL, nil) + _require.NoError(err) + + _, err = fileSASClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) + + _, err = fileSASClient.Create(context.Background(), 1024, nil) + _require.NoError(err) + + resp, err := fileSASClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) +} + +func (f *FileRecordedTestsSuite) TestFileCreateDeleteDefault() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fileName := testcommon.GenerateFileName(testName) + rootDirClient := shareClient.NewRootDirectoryClient() + _require.NoError(err) + + fClient := rootDirClient.NewFileClient(fileName) + + // Create and delete file in root directory. + cResp, err := fClient.Create(context.Background(), 1024, nil) + _require.NoError(err) + _require.NotNil(cResp.ETag) + _require.Equal(cResp.LastModified.IsZero(), false) + _require.NotNil(cResp.RequestID) + _require.NotNil(cResp.Version) + _require.Equal(cResp.Date.IsZero(), false) + _require.NotNil(cResp.IsServerEncrypted) + + delResp, err := fClient.Delete(context.Background(), nil) + _require.NoError(err) + _require.NotNil(delResp.RequestID) + _require.NotNil(delResp.Version) + _require.Equal(delResp.Date.IsZero(), false) + + dirClient := testcommon.CreateNewDirectory(context.Background(), _require, testcommon.GenerateDirectoryName(testName), shareClient) + + // Create and delete file in named directory. + afClient := dirClient.NewFileClient(fileName) + + cResp, err = afClient.Create(context.Background(), 1024, nil) + _require.NoError(err) + _require.NotNil(cResp.ETag) + _require.Equal(cResp.LastModified.IsZero(), false) + _require.NotNil(cResp.RequestID) + _require.NotNil(cResp.Version) + _require.Equal(cResp.Date.IsZero(), false) + _require.NotNil(cResp.IsServerEncrypted) + + delResp, err = afClient.Delete(context.Background(), nil) + _require.NoError(err) + _require.NotNil(delResp.RequestID) + _require.NotNil(delResp.Version) + _require.Equal(delResp.Date.IsZero(), false) +} + +func (f *FileRecordedTestsSuite) TestFileCreateNonDefaultMetadataNonEmpty() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 1024, &file.CreateOptions{ + Metadata: testcommon.BasicMetadata, + }) + _require.NoError(err) + + resp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Len(resp.Metadata, len(testcommon.BasicMetadata)) + for k, v := range resp.Metadata { + val := testcommon.BasicMetadata[strings.ToLower(k)] + _require.NotNil(val) + _require.Equal(*v, *val) + } +} + +func (f *FileRecordedTestsSuite) TestFileCreateNonDefaultHTTPHeaders() { + _require := require.New(f.T()) + testName := f.T().Name() + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + + httpHeaders := file.HTTPHeaders{ + ContentType: to.Ptr("my_type"), + ContentDisposition: to.Ptr("my_disposition"), + CacheControl: to.Ptr("control"), + ContentMD5: nil, + ContentLanguage: to.Ptr("my_language"), + ContentEncoding: to.Ptr("my_encoding"), + } + + _, err = fClient.Create(context.Background(), 1024, &file.CreateOptions{ + HTTPHeaders: &httpHeaders, + }) + _require.NoError(err) + + resp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(resp.ContentType, httpHeaders.ContentType) + _require.EqualValues(resp.ContentDisposition, httpHeaders.ContentDisposition) + _require.EqualValues(resp.CacheControl, httpHeaders.CacheControl) + _require.EqualValues(resp.ContentLanguage, httpHeaders.ContentLanguage) + _require.EqualValues(resp.ContentEncoding, httpHeaders.ContentEncoding) + _require.Nil(resp.ContentMD5) +} + +func (f *FileRecordedTestsSuite) TestFileCreateNegativeMetadataInvalid() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 1024, &file.CreateOptions{ + Metadata: map[string]*string{"!@#$%^&*()": to.Ptr("!@#$%^&*()")}, + HTTPHeaders: &file.HTTPHeaders{}, + }) + _require.Error(err) +} + +func (f *FileUnrecordedTestsSuite) TestFileGetSetPropertiesNonDefault() { + _require := require.New(f.T()) + testName := f.T().Name() + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + md5Str := "MDAwMDAwMDA=" + var testMd5 []byte + copy(testMd5[:], md5Str) + + creationTime := time.Now().Add(-time.Hour) + lastWriteTime := time.Now().Add(-time.Minute * 15) + + options := &file.SetHTTPHeadersOptions{ + Permissions: &file.Permissions{Permission: &testcommon.SampleSDDL}, + SMBProperties: &file.SMBProperties{ + Attributes: &file.NTFSFileAttributes{Hidden: true}, + CreationTime: &creationTime, + LastWriteTime: &lastWriteTime, + }, + HTTPHeaders: &file.HTTPHeaders{ + ContentType: to.Ptr("text/html"), + ContentEncoding: to.Ptr("gzip"), + ContentLanguage: to.Ptr("en"), + ContentMD5: testMd5, + CacheControl: to.Ptr("no-transform"), + ContentDisposition: to.Ptr("attachment"), + }, + } + setResp, err := fClient.SetHTTPHeaders(context.Background(), options) + _require.NoError(err) + _require.NotNil(setResp.ETag) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.NotNil(setResp.RequestID) + _require.NotNil(setResp.Version) + _require.Equal(setResp.Date.IsZero(), false) + _require.NotNil(setResp.IsServerEncrypted) + + getResp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.Equal(*getResp.FileType, "File") + + _require.EqualValues(getResp.ContentType, options.HTTPHeaders.ContentType) + _require.EqualValues(getResp.ContentEncoding, options.HTTPHeaders.ContentEncoding) + _require.EqualValues(getResp.ContentLanguage, options.HTTPHeaders.ContentLanguage) + _require.EqualValues(getResp.ContentMD5, options.HTTPHeaders.ContentMD5) + _require.EqualValues(getResp.CacheControl, options.HTTPHeaders.CacheControl) + _require.EqualValues(getResp.ContentDisposition, options.HTTPHeaders.ContentDisposition) + _require.Equal(*getResp.ContentLength, int64(0)) + // We'll just ensure a permission exists, no need to test overlapping functionality. + _require.NotEqual(getResp.FilePermissionKey, "") + _require.Equal(*getResp.FileAttributes, options.SMBProperties.Attributes.String()) + + _require.EqualValues(*getResp.FileCreationTime, creationTime.UTC()) + _require.EqualValues(*getResp.FileLastWriteTime, lastWriteTime.UTC()) + + _require.NotNil(getResp.ETag) + _require.NotNil(getResp.RequestID) + _require.NotNil(getResp.Version) + _require.Equal(getResp.Date.IsZero(), false) + _require.NotNil(getResp.IsServerEncrypted) +} + +func (f *FileRecordedTestsSuite) TestFilePreservePermissions() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, &file.CreateOptions{ + Permissions: &file.Permissions{ + Permission: &testcommon.SampleSDDL, + }, + }) + _require.NoError(err) + + // Grab the original perm key before we set file headers. + getResp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + + pKey := getResp.FilePermissionKey + cTime := getResp.FileCreationTime + lwTime := getResp.FileLastWriteTime + attribs := getResp.FileAttributes + + md5Str := "MDAwMDAwMDA=" + var testMd5 []byte + copy(testMd5[:], md5Str) + + properties := file.SetHTTPHeadersOptions{ + HTTPHeaders: &file.HTTPHeaders{ + ContentType: to.Ptr("text/html"), + ContentEncoding: to.Ptr("gzip"), + ContentLanguage: to.Ptr("en"), + ContentMD5: testMd5, + CacheControl: to.Ptr("no-transform"), + ContentDisposition: to.Ptr("attachment"), + }, + // SMBProperties, when options are left nil, leads to preserving. + SMBProperties: &file.SMBProperties{}, + } + + setResp, err := fClient.SetHTTPHeaders(context.Background(), &properties) + _require.NoError(err) + _require.NotNil(setResp.ETag) + _require.NotNil(setResp.RequestID) + _require.NotNil(setResp.LastModified) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.NotNil(setResp.Version) + _require.Equal(setResp.Date.IsZero(), false) + + getResp, err = fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(setResp.LastModified) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.Equal(*getResp.FileType, "File") + + _require.EqualValues(getResp.ContentType, properties.HTTPHeaders.ContentType) + _require.EqualValues(getResp.ContentEncoding, properties.HTTPHeaders.ContentEncoding) + _require.EqualValues(getResp.ContentLanguage, properties.HTTPHeaders.ContentLanguage) + _require.EqualValues(getResp.ContentMD5, properties.HTTPHeaders.ContentMD5) + _require.EqualValues(getResp.CacheControl, properties.HTTPHeaders.CacheControl) + _require.EqualValues(getResp.ContentDisposition, properties.HTTPHeaders.ContentDisposition) + _require.Equal(*getResp.ContentLength, int64(0)) + // Ensure that the permission key gets preserved + _require.EqualValues(getResp.FilePermissionKey, pKey) + _require.EqualValues(cTime, getResp.FileCreationTime) + _require.EqualValues(lwTime, getResp.FileLastWriteTime) + _require.EqualValues(attribs, getResp.FileAttributes) + + _require.NotNil(getResp.ETag) + _require.NotNil(getResp.RequestID) + _require.NotNil(getResp.Version) + _require.Equal(getResp.Date.IsZero(), false) + _require.NotNil(getResp.IsServerEncrypted) +} + +func (f *FileRecordedTestsSuite) TestFileGetSetPropertiesSnapshot() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer func() { + _, err := shareClient.Delete(context.Background(), &share.DeleteOptions{DeleteSnapshots: to.Ptr(share.DeleteSnapshotsOptionTypeInclude)}) + _require.NoError(err) + }() + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + md5Str := "MDAwMDAwMDA=" + var testMd5 []byte + copy(testMd5[:], md5Str) + + fileSetHTTPHeadersOptions := file.SetHTTPHeadersOptions{ + HTTPHeaders: &file.HTTPHeaders{ + ContentType: to.Ptr("text/html"), + ContentEncoding: to.Ptr("gzip"), + ContentLanguage: to.Ptr("en"), + ContentMD5: testMd5, + CacheControl: to.Ptr("no-transform"), + ContentDisposition: to.Ptr("attachment"), + }, + } + setResp, err := fClient.SetHTTPHeaders(context.Background(), &fileSetHTTPHeadersOptions) + _require.NoError(err) + _require.NotEqual(*setResp.ETag, "") + _require.Equal(setResp.LastModified.IsZero(), false) + _require.NotEqual(setResp.RequestID, "") + _require.NotEqual(setResp.Version, "") + _require.Equal(setResp.Date.IsZero(), false) + _require.NotNil(setResp.IsServerEncrypted) + + metadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + _, err = fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: metadata, + }) + _require.NoError(err) + + resp, err := shareClient.CreateSnapshot(context.Background(), &share.CreateSnapshotOptions{Metadata: map[string]*string{}}) + _require.NoError(err) + _require.NotNil(resp.Snapshot) + + // get properties on the share snapshot + getResp, err := fClient.GetProperties(context.Background(), &file.GetPropertiesOptions{ + ShareSnapshot: resp.Snapshot, + }) + _require.NoError(err) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.Equal(*getResp.FileType, "File") + + _require.EqualValues(getResp.ContentType, fileSetHTTPHeadersOptions.HTTPHeaders.ContentType) + _require.EqualValues(getResp.ContentEncoding, fileSetHTTPHeadersOptions.HTTPHeaders.ContentEncoding) + _require.EqualValues(getResp.ContentLanguage, fileSetHTTPHeadersOptions.HTTPHeaders.ContentLanguage) + _require.EqualValues(getResp.ContentMD5, fileSetHTTPHeadersOptions.HTTPHeaders.ContentMD5) + _require.EqualValues(getResp.CacheControl, fileSetHTTPHeadersOptions.HTTPHeaders.CacheControl) + _require.EqualValues(getResp.ContentDisposition, fileSetHTTPHeadersOptions.HTTPHeaders.ContentDisposition) + _require.Equal(*getResp.ContentLength, int64(0)) + + _require.NotNil(getResp.ETag) + _require.NotNil(getResp.RequestID) + _require.NotNil(getResp.Version) + _require.Equal(getResp.Date.IsZero(), false) + _require.NotNil(getResp.IsServerEncrypted) + _require.EqualValues(getResp.Metadata, metadata) +} + +func (f *FileRecordedTestsSuite) TestGetSetMetadataNonDefault() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + metadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + setResp, err := fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: metadata, + }) + _require.NoError(err) + _require.NotNil(setResp.ETag) + _require.NotNil(setResp.RequestID) + _require.NotNil(setResp.Version) + _require.Equal(setResp.Date.IsZero(), false) + _require.NotNil(setResp.IsServerEncrypted) + + getResp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(getResp.ETag) + _require.NotNil(getResp.RequestID) + _require.NotNil(getResp.Version) + _require.Equal(getResp.Date.IsZero(), false) + _require.NotNil(getResp.IsServerEncrypted) + _require.EqualValues(getResp.Metadata, metadata) +} + +func (f *FileRecordedTestsSuite) TestFileSetMetadataNil() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + md := map[string]*string{"Not": to.Ptr("nil")} + + _, err = fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: md, + }) + _require.NoError(err) + + resp1, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(resp1.Metadata, md) + + _, err = fClient.SetMetadata(context.Background(), nil) + _require.NoError(err) + + resp2, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Len(resp2.Metadata, 0) +} + +func (f *FileRecordedTestsSuite) TestFileSetMetadataDefaultEmpty() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + md := map[string]*string{"Not": to.Ptr("nil")} + + _, err = fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: md, + }) + _require.NoError(err) + + resp1, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(resp1.Metadata, md) + + _, err = fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: map[string]*string{}, + }) + _require.NoError(err) + + resp2, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Len(resp2.Metadata, 0) +} + +func (f *FileRecordedTestsSuite) TestFileSetMetadataInvalidField() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + _, err = fClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: map[string]*string{"!@#$%^&*()": to.Ptr("!@#$%^&*()")}, + }) + _require.Error(err) +} + +func waitForCopy(_require *require.Assertions, copyFClient *file.Client, fileCopyResponse file.StartCopyFromURLResponse) { + status := fileCopyResponse.CopyStatus + // Wait for the copy to finish. If the copy takes longer than a minute, we will fail + start := time.Now() + for *status != file.CopyStatusTypeSuccess { + GetPropertiesResp, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + status = GetPropertiesResp.CopyStatus + currentTime := time.Now() + if currentTime.Sub(start) >= time.Minute && *status != file.CopyStatusTypeSuccess { + _require.Fail("Copy status is " + string(*status) + "after 1 minute") + } + } +} + +func (f *FileUnrecordedTestsSuite) TestFileStartCopyMetadata() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient("dst" + testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + basicMetadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + resp, err := copyFClient.StartCopyFromURL(context.Background(), fClient.URL(), &file.StartCopyFromURLOptions{Metadata: basicMetadata}) + _require.NoError(err) + waitForCopy(_require, copyFClient, resp) + + resp2, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(resp2.Metadata, basicMetadata) +} + +func (f *FileUnrecordedTestsSuite) TestFileStartCopyMetadataNil() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient("dst" + testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + basicMetadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + + // Have the destination start with metadata so we ensure the nil metadata passed later takes effect + _, err = copyFClient.Create(context.Background(), 0, &file.CreateOptions{Metadata: basicMetadata}) + _require.NoError(err) + + gResp, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(gResp.Metadata, basicMetadata) + + resp, err := copyFClient.StartCopyFromURL(context.Background(), fClient.URL(), nil) + _require.NoError(err) + + waitForCopy(_require, copyFClient, resp) + + resp2, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Len(resp2.Metadata, 0) +} + +func (f *FileUnrecordedTestsSuite) TestFileStartCopyMetadataEmpty() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient("dst" + testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + basicMetadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + + // Have the destination start with metadata so we ensure the nil metadata passed later takes effect + _, err = copyFClient.Create(context.Background(), 0, &file.CreateOptions{Metadata: basicMetadata}) + _require.NoError(err) + + gResp, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.EqualValues(gResp.Metadata, basicMetadata) + + resp, err := copyFClient.StartCopyFromURL(context.Background(), fClient.URL(), &file.StartCopyFromURLOptions{Metadata: map[string]*string{}}) + _require.NoError(err) + + waitForCopy(_require, copyFClient, resp) + + resp2, err := copyFClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Len(resp2.Metadata, 0) +} + +func (f *FileRecordedTestsSuite) TestFileStartCopyNegativeMetadataInvalidField() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient("dst" + testcommon.GenerateFileName(testName)) + + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + _, err = copyFClient.StartCopyFromURL(context.Background(), fClient.URL(), &file.StartCopyFromURLOptions{ + Metadata: map[string]*string{"!@#$%^&*()": to.Ptr("!@#$%^&*()")}, + }) + _require.Error(err) +} + +func (f *FileRecordedTestsSuite) TestFileStartCopySourceNonExistent() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient("dst" + testcommon.GenerateFileName(testName)) + + _, err = copyFClient.StartCopyFromURL(context.Background(), fClient.URL(), nil) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) +} + +func (f *FileRecordedTestsSuite) TestFileAbortCopyNoCopyStarted() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + copyFClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = copyFClient.AbortCopy(context.Background(), "copynotstarted", nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidQueryParameterValue) +} + +func (f *FileRecordedTestsSuite) TestResizeFile() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 1234, nil) + _require.NoError(err) + + gResp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(*gResp.ContentLength, int64(1234)) + + _, err = fClient.Resize(context.Background(), 4096, nil) + _require.NoError(err) + + gResp, err = fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(*gResp.ContentLength, int64(4096)) +} + +func (f *FileRecordedTestsSuite) TestFileResizeZero() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 10, nil) + _require.NoError(err) + + _, err = fClient.Resize(context.Background(), 0, nil) + _require.NoError(err) + + resp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(*resp.ContentLength, int64(0)) +} + +func (f *FileRecordedTestsSuite) TestFileResizeInvalidSizeNegative() { + _require := require.New(f.T()) + testName := f.T().Name() + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareClient := testcommon.CreateNewShare(context.Background(), _require, testcommon.GenerateShareName(testName), svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient("src" + testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + _, err = fClient.Resize(context.Background(), -4, nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.OutOfRangeInput) +} + +func (f *FileRecordedTestsSuite) TestNegativeFileSizeMoreThanShareQuota() { + _require := require.New(f.T()) + testName := f.T().Name() + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + var fileShareMaxQuota int32 = 1024 // share size in GiB which is 1TiB + var fileMaxAllowedSizeInBytes int64 = 4398046511104 // file size in bytes which is 4 TiB + + shareClient := testcommon.GetShareClient(testcommon.GenerateShareName(testName), svcClient) + _, err = shareClient.Create(context.Background(), &share.CreateOptions{ + Quota: &fileShareMaxQuota, + }) + _require.NoError(err) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), fileMaxAllowedSizeInBytes, &file.CreateOptions{ + HTTPHeaders: &file.HTTPHeaders{}, + }) + _require.Error(err) +} + +func (f *FileRecordedTestsSuite) TestCreateMaximumSizeFileShare() { + _require := require.New(f.T()) + testName := f.T().Name() + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + var fileShareMaxQuota int32 = 5120 // share size in GiB which is 5TiB + var fileMaxAllowedSizeInBytes int64 = 4398046511104 // file size in bytes which is 4 TiB + + shareClient := testcommon.GetShareClient(testcommon.GenerateShareName(testName), svcClient) + _, err = shareClient.Create(context.Background(), &share.CreateOptions{ + Quota: &fileShareMaxQuota, + }) + _require.NoError(err) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fClient := shareClient.NewRootDirectoryClient().NewFileClient(testcommon.GenerateFileName(testName)) + _, err = fClient.Create(context.Background(), fileMaxAllowedSizeInBytes, &file.CreateOptions{ + HTTPHeaders: &file.HTTPHeaders{}, + }) + _require.NoError(err) +} + +func (f *FileRecordedTestsSuite) TestSASFileClientNoKey() { + _require := require.New(f.T()) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + testName := f.T().Name() + shareName := testcommon.GenerateShareName(testName) + fileName := testcommon.GenerateFileName(testName) + fileClient, err := file.NewClientWithNoCredential(fmt.Sprintf("https://%s.file.core.windows.net/%v/%v", accountName, shareName, fileName), nil) + _require.NoError(err) + + permissions := sas.FilePermissions{ + Read: true, + Write: true, + Delete: true, + Create: true, + } + expiry := time.Now().Add(time.Hour) + + _, err = fileClient.GetSASURL(permissions, expiry, nil) + _require.Equal(err, fileerror.MissingSharedKeyCredential) +} + +func (f *FileRecordedTestsSuite) TestSASFileClientSignNegative() { + _require := require.New(f.T()) + accountName, accountKey := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + _require.Greater(len(accountKey), 0) + + cred, err := service.NewSharedKeyCredential(accountName, accountKey) + _require.NoError(err) + + testName := f.T().Name() + shareName := testcommon.GenerateShareName(testName) + fileName := testcommon.GenerateFileName(testName) + fileClient, err := file.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.file.core.windows.net/%v%v", accountName, shareName, fileName), cred, nil) + _require.NoError(err) + + permissions := sas.FilePermissions{ + Read: true, + Write: true, + Delete: true, + Create: true, + } + expiry := time.Time{} + + _, err = fileClient.GetSASURL(permissions, expiry, nil) + _require.Equal(err.Error(), "service SAS is missing at least one of these: ExpiryTime or Permissions") +} + +// TODO: Add tests for different options of StartCopyFromURL() + +// TODO: Add tests for upload and download methods + +// TODO: Add tests for GetRangeList, ListHandles and ForceCloseHandles diff --git a/sdk/storage/azfile/file/models.go b/sdk/storage/azfile/file/models.go index 890959ca86f5..8ffdfdcbba84 100644 --- a/sdk/storage/azfile/file/models.go +++ b/sdk/storage/azfile/file/models.go @@ -7,8 +7,11 @@ package file import ( + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/generated" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/shared" + "time" ) // SharedKeyCredential contains an account's name and its primary or secondary key. @@ -39,10 +42,7 @@ type CopyFileSMBInfo = generated.CopyFileSMBInfo // HTTPRange defines a range of bytes within an HTTP resource, starting at offset and // ending at offset+count. A zero-value HTTPRange indicates the entire resource. An HTTPRange // which has an offset but no zero value count indicates from the offset to the resource's end. -type HTTPRange struct { - Offset int64 - Count int64 -} +type HTTPRange = exported.HTTPRange // ShareFileRangeList - The list of file ranges. type ShareFileRangeList = generated.ShareFileRangeList @@ -58,7 +58,6 @@ type ShareFileRange = generated.FileRange // CreateOptions contains the optional parameters for the Client.Create method. type CreateOptions struct { // The default value is 'None' for Attributes and 'now' for CreationTime and LastWriteTime fields in file.SMBProperties. - // TODO: Change the types of creation time and last write time to string from time.Time to include values like 'now', 'preserve', etc. SMBProperties *SMBProperties // The default value is 'inherit' for Permission field in file.Permissions. Permissions *Permissions @@ -68,6 +67,30 @@ type CreateOptions struct { Metadata map[string]*string } +func (o *CreateOptions) format() (fileAttributes string, fileCreationTime string, fileLastWriteTime string, + createOptions *generated.FileClientCreateOptions, fileHTTPHeaders *generated.ShareFileHTTPHeaders, leaseAccessConditions *LeaseAccessConditions) { + if o == nil { + return shared.FileAttributesNone, shared.DefaultCurrentTimeString, shared.DefaultCurrentTimeString, &generated.FileClientCreateOptions{ + FilePermission: to.Ptr(shared.DefaultFilePermissionString), + }, nil, nil + } + + fileAttributes, fileCreationTime, fileLastWriteTime = o.SMBProperties.Format(false, shared.FileAttributesNone, shared.DefaultCurrentTimeString) + + permission, permissionKey := o.Permissions.Format(shared.DefaultFilePermissionString) + + createOptions = &generated.FileClientCreateOptions{ + FilePermission: permission, + FilePermissionKey: permissionKey, + Metadata: o.Metadata, + } + + fileHTTPHeaders = o.HTTPHeaders + leaseAccessConditions = o.LeaseAccessConditions + + return +} + // --------------------------------------------------------------------------------------------------------------------- // DeleteOptions contains the optional parameters for the Client.Delete method. @@ -76,6 +99,13 @@ type DeleteOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *DeleteOptions) format() (*generated.FileClientDeleteOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + return nil, o.LeaseAccessConditions +} + // --------------------------------------------------------------------------------------------------------------------- // GetPropertiesOptions contains the optional parameters for the Client.GetProperties method. @@ -86,6 +116,16 @@ type GetPropertiesOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *GetPropertiesOptions) format() (*generated.FileClientGetPropertiesOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + + return &generated.FileClientGetPropertiesOptions{ + Sharesnapshot: o.ShareSnapshot, + }, o.LeaseAccessConditions +} + // --------------------------------------------------------------------------------------------------------------------- // SetHTTPHeadersOptions contains the optional parameters for the Client.SetHTTPHeaders method. @@ -94,7 +134,6 @@ type SetHTTPHeadersOptions struct { // above the specified byte value are cleared. FileContentLength *int64 // The default value is 'preserve' for Attributes, CreationTime and LastWriteTime fields in file.SMBProperties. - // TODO: Change the types of creation time and last write time to string from time.Time to include values like 'now', 'preserve', etc. SMBProperties *SMBProperties // The default value is 'preserve' for Permission field in file.Permissions. Permissions *Permissions @@ -103,6 +142,30 @@ type SetHTTPHeadersOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *SetHTTPHeadersOptions) format() (fileAttributes string, fileCreationTime string, fileLastWriteTime string, + opts *generated.FileClientSetHTTPHeadersOptions, fileHTTPHeaders *generated.ShareFileHTTPHeaders, leaseAccessConditions *LeaseAccessConditions) { + if o == nil { + return shared.DefaultPreserveString, shared.DefaultPreserveString, shared.DefaultPreserveString, &generated.FileClientSetHTTPHeadersOptions{ + FilePermission: to.Ptr(shared.DefaultPreserveString), + }, nil, nil + } + + fileAttributes, fileCreationTime, fileLastWriteTime = o.SMBProperties.Format(false, shared.DefaultPreserveString, shared.DefaultPreserveString) + + permission, permissionKey := o.Permissions.Format(shared.DefaultPreserveString) + + opts = &generated.FileClientSetHTTPHeadersOptions{ + FileContentLength: o.FileContentLength, + FilePermission: permission, + FilePermissionKey: permissionKey, + } + + fileHTTPHeaders = o.HTTPHeaders + leaseAccessConditions = o.LeaseAccessConditions + + return +} + // --------------------------------------------------------------------------------------------------------------------- // SetMetadataOptions contains the optional parameters for the Client.SetMetadata method. @@ -113,6 +176,15 @@ type SetMetadataOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *SetMetadataOptions) format() (*generated.FileClientSetMetadataOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + return &generated.FileClientSetMetadataOptions{ + Metadata: o.Metadata, + }, o.LeaseAccessConditions +} + // --------------------------------------------------------------------------------------------------------------------- // StartCopyFromURLOptions contains the optional parameters for the Client.StartCopyFromURL method. @@ -127,6 +199,26 @@ type StartCopyFromURLOptions struct { LeaseAccessConditions *LeaseAccessConditions } +// TODO: discuss on the types of FileAttributes, FileCreationTime and FileLastWriteTime in CopyFileSMBInfo. +func (o *StartCopyFromURLOptions) format() (*generated.FileClientStartCopyOptions, *generated.CopyFileSMBInfo, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil, nil + } + + var permission, permissionKey *string + if o.Permissions != nil { + permission = o.Permissions.Permission + permissionKey = o.Permissions.PermissionKey + } + + opts := &generated.FileClientStartCopyOptions{ + FilePermission: permission, + FilePermissionKey: permissionKey, + Metadata: o.Metadata, + } + return opts, o.CopyFileSMBInfo, o.LeaseAccessConditions +} + // --------------------------------------------------------------------------------------------------------------------- // AbortCopyOptions contains the optional parameters for the Client.AbortCopy method. @@ -136,6 +228,14 @@ type AbortCopyOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *AbortCopyOptions) format() (*generated.FileClientAbortCopyOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + + return nil, o.LeaseAccessConditions +} + // --------------------------------------------------------------------------------------------------------------------- // DownloadStreamOptions contains the optional parameters for the Client.DownloadStream method. @@ -214,6 +314,22 @@ type ResizeOptions struct { LeaseAccessConditions *LeaseAccessConditions } +func (o *ResizeOptions) format(contentLength int64) (fileAttributes string, fileCreationTime string, fileLastWriteTime string, + opts *generated.FileClientSetHTTPHeadersOptions, leaseAccessConditions *LeaseAccessConditions) { + fileAttributes, fileCreationTime, fileLastWriteTime = shared.DefaultPreserveString, shared.DefaultPreserveString, shared.DefaultPreserveString + + opts = &generated.FileClientSetHTTPHeadersOptions{ + FileContentLength: &contentLength, + FilePermission: to.Ptr(shared.DefaultPreserveString), + } + + if o != nil { + leaseAccessConditions = o.LeaseAccessConditions + } + + return +} + // --------------------------------------------------------------------------------------------------------------------- // UploadRangeOptions contains the optional parameters for the Client.UploadRange method. @@ -257,9 +373,97 @@ type GetRangeListOptions struct { // The previous snapshot parameter is an opaque DateTime value that, when present, specifies the previous snapshot. PrevShareSnapshot *string // Specifies the range of bytes over which to list ranges, inclusively. - Range *HTTPRange + Range HTTPRange // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. ShareSnapshot *string // LeaseAccessConditions contains optional parameters to access leased entity. LeaseAccessConditions *LeaseAccessConditions } + +func (o *GetRangeListOptions) format() (*generated.FileClientGetRangeListOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + + return &generated.FileClientGetRangeListOptions{ + Prevsharesnapshot: o.PrevShareSnapshot, + Range: exported.FormatHTTPRange(o.Range), + Sharesnapshot: o.ShareSnapshot, + }, o.LeaseAccessConditions +} + +// --------------------------------------------------------------------------------------------------------------------- + +// GetSASURLOptions contains the optional parameters for the Client.GetSASURL method. +type GetSASURLOptions struct { + StartTime *time.Time +} + +func (o *GetSASURLOptions) format() time.Time { + if o == nil { + return time.Time{} + } + + var st time.Time + if o.StartTime != nil { + st = o.StartTime.UTC() + } else { + st = time.Time{} + } + return st +} + +// --------------------------------------------------------------------------------------------------------------------- + +// ForceCloseHandlesOptions contains the optional parameters for the Client.ForceCloseHandles method. +type ForceCloseHandlesOptions struct { + // A string value that identifies the portion of the list to be returned with the next list operation. The operation returns + // a marker value within the response body if the list returned was not complete. + // The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque + // to the client. + Marker *string + // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. + ShareSnapshot *string +} + +func (o *ForceCloseHandlesOptions) format() *generated.FileClientForceCloseHandlesOptions { + if o == nil { + return nil + } + + return &generated.FileClientForceCloseHandlesOptions{ + Marker: o.Marker, + Sharesnapshot: o.ShareSnapshot, + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +// ListHandlesOptions contains the optional parameters for the Client.ListHandles method. +type ListHandlesOptions struct { + // A string value that identifies the portion of the list to be returned with the next list operation. The operation returns + // a marker value within the response body if the list returned was not complete. + // The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque + // to the client. + Marker *string + // Specifies the maximum number of entries to return. If the request does not specify maxresults, or specifies a value greater + // than 5,000, the server will return up to 5,000 items. + MaxResults *int32 + // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. + ShareSnapshot *string +} + +func (o *ListHandlesOptions) format() *generated.FileClientListHandlesOptions { + if o == nil { + return nil + } + + return &generated.FileClientListHandlesOptions{ + Marker: o.Marker, + Maxresults: o.MaxResults, + Sharesnapshot: o.ShareSnapshot, + } +} + +// Handle - A listed Azure Storage handle item. +type Handle = generated.Handle diff --git a/sdk/storage/azfile/file/responses.go b/sdk/storage/azfile/file/responses.go index 2c037fe8b996..3101c3c9f4ff 100644 --- a/sdk/storage/azfile/file/responses.go +++ b/sdk/storage/azfile/file/responses.go @@ -54,3 +54,12 @@ type UploadRangeFromURLResponse = generated.FileClientUploadRangeFromURLResponse // GetRangeListResponse contains the response from method Client.GetRangeList. type GetRangeListResponse = generated.FileClientGetRangeListResponse + +// ForceCloseHandlesResponse contains the response from method Client.ForceCloseHandles. +type ForceCloseHandlesResponse = generated.FileClientForceCloseHandlesResponse + +// ListHandlesResponse contains the response from method Client.ListHandles. +type ListHandlesResponse = generated.FileClientListHandlesResponse + +// ListHandlesSegmentResponse - An enumeration of handles. +type ListHandlesSegmentResponse = generated.ListHandlesResponse diff --git a/sdk/storage/azfile/internal/exported/exported.go b/sdk/storage/azfile/internal/exported/exported.go new file mode 100644 index 000000000000..9bc1ca47df84 --- /dev/null +++ b/sdk/storage/azfile/internal/exported/exported.go @@ -0,0 +1,33 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package exported + +import ( + "fmt" + "strconv" +) + +// HTTPRange defines a range of bytes within an HTTP resource, starting at offset and +// ending at offset+count. A zero-value HTTPRange indicates the entire resource. An HTTPRange +// which has an offset but no zero value count indicates from the offset to the resource's end. +type HTTPRange struct { + Offset int64 + Count int64 +} + +// FormatHTTPRange converts an HTTPRange to its string format. +func FormatHTTPRange(r HTTPRange) *string { + if r.Offset == 0 && r.Count == 0 { + return nil // No specified range + } + endOffset := "" // if count == CountToEnd (0) + if r.Count > 0 { + endOffset = strconv.FormatInt((r.Offset+r.Count)-1, 10) + } + dataRange := fmt.Sprintf("bytes=%v-%s", r.Offset, endOffset) + return &dataRange +} diff --git a/sdk/storage/azfile/internal/generated/autorest.md b/sdk/storage/azfile/internal/generated/autorest.md index ec6514bc1a44..634ccff33f46 100644 --- a/sdk/storage/azfile/internal/generated/autorest.md +++ b/sdk/storage/azfile/internal/generated/autorest.md @@ -276,7 +276,9 @@ directive: ``` yaml directive: - - from: zz_directory_client.go + - from: + - zz_directory_client.go + - zz_file_client.go where: $ transform: >- return $. @@ -284,3 +286,24 @@ directive: replace(/fileLastWriteTime,\s+err\s+\:=\s+time\.Parse\(time\.RFC1123,\s+val\)/g, `fileLastWriteTime, err := time.Parse(ISO8601, val)`). replace(/fileChangeTime,\s+err\s+\:=\s+time\.Parse\(time\.RFC1123,\s+val\)/g, `fileChangeTime, err := time.Parse(ISO8601, val)`); ``` + +### Change `Duration` parameter in leases to be required + +``` yaml +directive: +- from: swagger-document + where: $.parameters.LeaseDuration + transform: > + $.required = true; +``` + +### Convert ShareUsageBytes to int64 + +``` yaml +directive: + - from: zz_models.go + where: $ + transform: >- + return $. + replace(/ShareUsageBytes\s+\*int32/g, `ShareUsageBytes *int64`); +``` diff --git a/sdk/storage/azfile/internal/generated/zz_file_client.go b/sdk/storage/azfile/internal/generated/zz_file_client.go index 398f0741fc8d..cfe2ea780a3b 100644 --- a/sdk/storage/azfile/internal/generated/zz_file_client.go +++ b/sdk/storage/azfile/internal/generated/zz_file_client.go @@ -108,9 +108,12 @@ func (client *FileClient) abortCopyHandleResponse(resp *http.Response) (FileClie // If the operation fails it returns an *azcore.ResponseError type. // // Generated from API version 2020-10-02 +// - duration - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite +// lease can be between 15 and 60 seconds. A lease duration cannot be changed using +// renew or change. // - options - FileClientAcquireLeaseOptions contains the optional parameters for the FileClient.AcquireLease method. -func (client *FileClient) AcquireLease(ctx context.Context, options *FileClientAcquireLeaseOptions) (FileClientAcquireLeaseResponse, error) { - req, err := client.acquireLeaseCreateRequest(ctx, options) +func (client *FileClient) AcquireLease(ctx context.Context, duration int32, options *FileClientAcquireLeaseOptions) (FileClientAcquireLeaseResponse, error) { + req, err := client.acquireLeaseCreateRequest(ctx, duration, options) if err != nil { return FileClientAcquireLeaseResponse{}, err } @@ -125,7 +128,7 @@ func (client *FileClient) AcquireLease(ctx context.Context, options *FileClientA } // acquireLeaseCreateRequest creates the AcquireLease request. -func (client *FileClient) acquireLeaseCreateRequest(ctx context.Context, options *FileClientAcquireLeaseOptions) (*policy.Request, error) { +func (client *FileClient) acquireLeaseCreateRequest(ctx context.Context, duration int32, options *FileClientAcquireLeaseOptions) (*policy.Request, error) { req, err := runtime.NewRequest(ctx, http.MethodPut, client.endpoint) if err != nil { return nil, err @@ -137,9 +140,7 @@ func (client *FileClient) acquireLeaseCreateRequest(ctx context.Context, options } req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["x-ms-lease-action"] = []string{"acquire"} - if options != nil && options.Duration != nil { - req.Raw().Header["x-ms-lease-duration"] = []string{strconv.FormatInt(int64(*options.Duration), 10)} - } + req.Raw().Header["x-ms-lease-duration"] = []string{strconv.FormatInt(int64(duration), 10)} if options != nil && options.ProposedLeaseID != nil { req.Raw().Header["x-ms-proposed-lease-id"] = []string{*options.ProposedLeaseID} } @@ -469,21 +470,21 @@ func (client *FileClient) createHandleResponse(resp *http.Response) (FileClientC result.FileAttributes = &val } if val := resp.Header.Get("x-ms-file-creation-time"); val != "" { - fileCreationTime, err := time.Parse(time.RFC1123, val) + fileCreationTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientCreateResponse{}, err } result.FileCreationTime = &fileCreationTime } if val := resp.Header.Get("x-ms-file-last-write-time"); val != "" { - fileLastWriteTime, err := time.Parse(time.RFC1123, val) + fileLastWriteTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientCreateResponse{}, err } result.FileLastWriteTime = &fileLastWriteTime } if val := resp.Header.Get("x-ms-file-change-time"); val != "" { - fileChangeTime, err := time.Parse(time.RFC1123, val) + fileChangeTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientCreateResponse{}, err } @@ -713,21 +714,21 @@ func (client *FileClient) downloadHandleResponse(resp *http.Response) (FileClien result.FileAttributes = &val } if val := resp.Header.Get("x-ms-file-creation-time"); val != "" { - fileCreationTime, err := time.Parse(time.RFC1123, val) + fileCreationTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientDownloadResponse{}, err } result.FileCreationTime = &fileCreationTime } if val := resp.Header.Get("x-ms-file-last-write-time"); val != "" { - fileLastWriteTime, err := time.Parse(time.RFC1123, val) + fileLastWriteTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientDownloadResponse{}, err } result.FileLastWriteTime = &fileLastWriteTime } if val := resp.Header.Get("x-ms-file-change-time"); val != "" { - fileChangeTime, err := time.Parse(time.RFC1123, val) + fileChangeTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientDownloadResponse{}, err } @@ -981,21 +982,21 @@ func (client *FileClient) getPropertiesHandleResponse(resp *http.Response) (File result.FileAttributes = &val } if val := resp.Header.Get("x-ms-file-creation-time"); val != "" { - fileCreationTime, err := time.Parse(time.RFC1123, val) + fileCreationTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientGetPropertiesResponse{}, err } result.FileCreationTime = &fileCreationTime } if val := resp.Header.Get("x-ms-file-last-write-time"); val != "" { - fileLastWriteTime, err := time.Parse(time.RFC1123, val) + fileLastWriteTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientGetPropertiesResponse{}, err } result.FileLastWriteTime = &fileLastWriteTime } if val := resp.Header.Get("x-ms-file-change-time"); val != "" { - fileChangeTime, err := time.Parse(time.RFC1123, val) + fileChangeTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientGetPropertiesResponse{}, err } @@ -1373,21 +1374,21 @@ func (client *FileClient) setHTTPHeadersHandleResponse(resp *http.Response) (Fil result.FileAttributes = &val } if val := resp.Header.Get("x-ms-file-creation-time"); val != "" { - fileCreationTime, err := time.Parse(time.RFC1123, val) + fileCreationTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientSetHTTPHeadersResponse{}, err } result.FileCreationTime = &fileCreationTime } if val := resp.Header.Get("x-ms-file-last-write-time"); val != "" { - fileLastWriteTime, err := time.Parse(time.RFC1123, val) + fileLastWriteTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientSetHTTPHeadersResponse{}, err } result.FileLastWriteTime = &fileLastWriteTime } if val := resp.Header.Get("x-ms-file-change-time"); val != "" { - fileChangeTime, err := time.Parse(time.RFC1123, val) + fileChangeTime, err := time.Parse(ISO8601, val) if err != nil { return FileClientSetHTTPHeadersResponse{}, err } diff --git a/sdk/storage/azfile/internal/generated/zz_models.go b/sdk/storage/azfile/internal/generated/zz_models.go index d328d388c9e0..95443aea430f 100644 --- a/sdk/storage/azfile/internal/generated/zz_models.go +++ b/sdk/storage/azfile/internal/generated/zz_models.go @@ -229,10 +229,6 @@ type FileClientAbortCopyOptions struct { // FileClientAcquireLeaseOptions contains the optional parameters for the FileClient.AcquireLease method. type FileClientAcquireLeaseOptions struct { - // Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite lease - // can be between 15 and 60 seconds. A lease duration cannot be changed using - // renew or change. - Duration *int32 // Proposed lease ID, in a GUID string format. The File service returns 400 (Invalid request) if the proposed lease ID is // not in the correct format. See Guid Constructor (String) for a list of valid GUID // string formats. @@ -636,10 +632,6 @@ type Share struct { // ShareClientAcquireLeaseOptions contains the optional parameters for the ShareClient.AcquireLease method. type ShareClientAcquireLeaseOptions struct { - // Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite lease - // can be between 15 and 60 seconds. A lease duration cannot be changed using - // renew or change. - Duration *int32 // Proposed lease ID, in a GUID string format. The File service returns 400 (Invalid request) if the proposed lease ID is // not in the correct format. See Guid Constructor (String) for a list of valid GUID // string formats. @@ -894,7 +886,7 @@ type ShareProperties struct { type ShareStats struct { // REQUIRED; The approximate size of the data stored in bytes. Note that this value may not include all recently created or // recently resized files. - ShareUsageBytes *int32 `xml:"ShareUsageBytes"` + ShareUsageBytes *int64 `xml:"ShareUsageBytes"` } // SignedIdentifier - Signed identifier. diff --git a/sdk/storage/azfile/internal/generated/zz_share_client.go b/sdk/storage/azfile/internal/generated/zz_share_client.go index 729f7c16801a..1ba2fda44963 100644 --- a/sdk/storage/azfile/internal/generated/zz_share_client.go +++ b/sdk/storage/azfile/internal/generated/zz_share_client.go @@ -45,9 +45,12 @@ func NewShareClient(endpoint string, pl runtime.Pipeline) *ShareClient { // If the operation fails it returns an *azcore.ResponseError type. // // Generated from API version 2020-10-02 +// - duration - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite +// lease can be between 15 and 60 seconds. A lease duration cannot be changed using +// renew or change. // - options - ShareClientAcquireLeaseOptions contains the optional parameters for the ShareClient.AcquireLease method. -func (client *ShareClient) AcquireLease(ctx context.Context, options *ShareClientAcquireLeaseOptions) (ShareClientAcquireLeaseResponse, error) { - req, err := client.acquireLeaseCreateRequest(ctx, options) +func (client *ShareClient) AcquireLease(ctx context.Context, duration int32, options *ShareClientAcquireLeaseOptions) (ShareClientAcquireLeaseResponse, error) { + req, err := client.acquireLeaseCreateRequest(ctx, duration, options) if err != nil { return ShareClientAcquireLeaseResponse{}, err } @@ -62,7 +65,7 @@ func (client *ShareClient) AcquireLease(ctx context.Context, options *ShareClien } // acquireLeaseCreateRequest creates the AcquireLease request. -func (client *ShareClient) acquireLeaseCreateRequest(ctx context.Context, options *ShareClientAcquireLeaseOptions) (*policy.Request, error) { +func (client *ShareClient) acquireLeaseCreateRequest(ctx context.Context, duration int32, options *ShareClientAcquireLeaseOptions) (*policy.Request, error) { req, err := runtime.NewRequest(ctx, http.MethodPut, client.endpoint) if err != nil { return nil, err @@ -78,9 +81,7 @@ func (client *ShareClient) acquireLeaseCreateRequest(ctx context.Context, option } req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["x-ms-lease-action"] = []string{"acquire"} - if options != nil && options.Duration != nil { - req.Raw().Header["x-ms-lease-duration"] = []string{strconv.FormatInt(int64(*options.Duration), 10)} - } + req.Raw().Header["x-ms-lease-duration"] = []string{strconv.FormatInt(int64(duration), 10)} if options != nil && options.ProposedLeaseID != nil { req.Raw().Header["x-ms-proposed-lease-id"] = []string{*options.ProposedLeaseID} } diff --git a/sdk/storage/azfile/internal/shared/shared.go b/sdk/storage/azfile/internal/shared/shared.go index 106e058ce55b..830f5dc512da 100644 --- a/sdk/storage/azfile/internal/shared/shared.go +++ b/sdk/storage/azfile/internal/shared/shared.go @@ -9,6 +9,8 @@ package shared import ( "errors" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/internal/uuid" "net" "strings" ) @@ -151,3 +153,14 @@ func IsIPEndpointStyle(host string) bool { } return net.ParseIP(host) != nil } + +func GenerateLeaseID(leaseID *string) (*string, error) { + if leaseID == nil { + generatedUuid, err := uuid.New() + if err != nil { + return nil, err + } + leaseID = to.Ptr(generatedUuid.String()) + } + return leaseID, nil +} diff --git a/sdk/storage/azfile/lease/client_test.go b/sdk/storage/azfile/lease/client_test.go new file mode 100644 index 000000000000..7b90fbfa9f7f --- /dev/null +++ b/sdk/storage/azfile/lease/client_test.go @@ -0,0 +1,633 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package lease_test + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/testcommon" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/lease" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/share" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "testing" + "time" +) + +func Test(t *testing.T) { + recordMode := recording.GetRecordMode() + t.Logf("Running lease Tests in %s mode\n", recordMode) + if recordMode == recording.LiveMode { + suite.Run(t, &LeaseRecordedTestsSuite{}) + suite.Run(t, &LeaseUnrecordedTestsSuite{}) + } else if recordMode == recording.PlaybackMode { + suite.Run(t, &LeaseRecordedTestsSuite{}) + } else if recordMode == recording.RecordingMode { + suite.Run(t, &LeaseRecordedTestsSuite{}) + } +} + +func (l *LeaseRecordedTestsSuite) BeforeTest(suite string, test string) { + testcommon.BeforeTest(l.T(), suite, test) +} + +func (l *LeaseRecordedTestsSuite) AfterTest(suite string, test string) { + testcommon.AfterTest(l.T(), suite, test) +} + +func (l *LeaseUnrecordedTestsSuite) BeforeTest(suite string, test string) { + +} + +func (l *LeaseUnrecordedTestsSuite) AfterTest(suite string, test string) { + +} + +type LeaseRecordedTestsSuite struct { + suite.Suite +} + +type LeaseUnrecordedTestsSuite struct { + suite.Suite +} + +var proposedLeaseIDs = []*string{to.Ptr("c820a799-76d7-4ee2-6e15-546f19325c2c"), to.Ptr("326cc5e1-746e-4af8-4811-a50e6629a8ca")} + +func (l *LeaseRecordedTestsSuite) TestShareAcquireLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + _, err = shareClient.Delete(ctx, nil) + _require.Error(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestNegativeShareAcquireMultipleLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient0, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + shareLeaseClient1, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[1], + }) + + ctx := context.Background() + acquireLeaseResponse0, err := shareLeaseClient0.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse0.LeaseID) + _require.EqualValues(*acquireLeaseResponse0.LeaseID, *shareLeaseClient0.LeaseID()) + + // acquiring lease for the second time returns LeaseAlreadyPresent error + _, err = shareLeaseClient1.Acquire(ctx, int32(60), nil) + _require.Error(err) + + _, err = shareLeaseClient0.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareDeleteShareWithoutLeaseId() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + _, err = shareClient.Delete(ctx, nil) + _require.Error(err) + + leaseID := shareLeaseClient.LeaseID() + _, err = shareClient.Delete(ctx, &share.DeleteOptions{ + LeaseAccessConditions: &share.LeaseAccessConditions{LeaseID: leaseID}, + }) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareReleaseLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + _, err = shareClient.Delete(ctx, nil) + _require.Error(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) + + _, err = shareClient.Delete(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareRenewLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(15), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + _, err = shareLeaseClient.Renew(ctx, nil) + _require.NoError(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareBreakLeaseDefault() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + bResp, err := shareLeaseClient.Break(ctx, nil) + _require.NoError(err) + _require.NotNil(bResp.LeaseTime) + + _, err = shareClient.Delete(ctx, nil) + _require.Error(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareBreakLeaseNonDefault() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + bResp, err := shareLeaseClient.Break(ctx, &lease.ShareBreakOptions{ + BreakPeriod: to.Ptr((int32)(5)), + }) + _require.NoError(err) + _require.NotNil(bResp.LeaseTime) + + _, err = shareClient.Delete(ctx, nil) + _require.Error(err) + + // wait for lease to expire + time.Sleep(6 * time.Second) + + _, err = shareClient.Delete(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestNegativeShareBreakRenewLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + bResp, err := shareLeaseClient.Break(ctx, &lease.ShareBreakOptions{ + BreakPeriod: to.Ptr((int32)(5)), + }) + _require.NoError(err) + _require.NotNil(bResp.LeaseTime) + + // renewing broken lease returns error + _, err = shareLeaseClient.Renew(ctx, nil) + _require.Error(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestShareChangeLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + shareLeaseClient, _ := lease.NewShareClient(shareClient, &lease.ShareClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + + ctx := context.Background() + acquireLeaseResponse, err := shareLeaseClient.Acquire(ctx, int32(60), nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(*acquireLeaseResponse.LeaseID, *shareLeaseClient.LeaseID()) + + oldLeaseID := shareLeaseClient.LeaseID() + + changeLeaseResp, err := shareLeaseClient.Change(ctx, *proposedLeaseIDs[1], nil) + _require.NoError(err) + _require.EqualValues(changeLeaseResp.LeaseID, proposedLeaseIDs[1]) + _require.EqualValues(shareLeaseClient.LeaseID(), proposedLeaseIDs[1]) + + _, err = shareClient.Delete(ctx, &share.DeleteOptions{ + LeaseAccessConditions: &share.LeaseAccessConditions{ + LeaseID: oldLeaseID, + }, + }) + _require.Error(err) + + _, err = shareLeaseClient.Renew(ctx, nil) + _require.NoError(err) + + _, err = shareLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestFileAcquireLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient.LeaseID()) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + _, err = fileLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestNegativeFileAcquireMultipleLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient0, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + fileLeaseClient1, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[1], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient0.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient0.LeaseID()) + + // acquiring lease for the second time returns LeaseAlreadyPresent error + _, err = fileLeaseClient1.Acquire(ctx, nil) + _require.Error(err) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + _, err = fileLeaseClient0.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestDeleteFileWithoutLeaseId() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient.LeaseID()) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + leaseID := fileLeaseClient.LeaseID() + _, err = fileClient.Delete(ctx, &file.DeleteOptions{ + LeaseAccessConditions: &file.LeaseAccessConditions{ + LeaseID: leaseID, + }, + }) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestFileReleaseLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient.LeaseID()) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + _, err = fileLeaseClient.Release(ctx, nil) + _require.NoError(err) + + _, err = fileClient.Delete(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestFileChangeLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.Equal(*acquireLeaseResponse.LeaseID, *proposedLeaseIDs[0]) + + oldLeaseID := fileLeaseClient.LeaseID() + + changeLeaseResp, err := fileLeaseClient.Change(ctx, *proposedLeaseIDs[1], nil) + _require.NoError(err) + _require.Equal(*changeLeaseResp.LeaseID, *proposedLeaseIDs[1]) + + _, err = fileClient.Delete(ctx, &file.DeleteOptions{ + LeaseAccessConditions: &file.LeaseAccessConditions{ + LeaseID: oldLeaseID, + }, + }) + _require.Error(err) + + _, err = fileLeaseClient.Release(ctx, nil) + _require.NoError(err) +} + +func (l *LeaseRecordedTestsSuite) TestNegativeFileDeleteAfterReleaseLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient.LeaseID()) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + _, err = fileLeaseClient.Release(ctx, nil) + _require.NoError(err) + + // deleting file after its lease has expired or released returns error. + _, err = fileClient.Delete(ctx, &file.DeleteOptions{ + LeaseAccessConditions: &file.LeaseAccessConditions{ + LeaseID: fileLeaseClient.LeaseID(), + }, + }) + _require.Error(err) +} + +func (l *LeaseRecordedTestsSuite) TestFileBreakLease() { + _require := require.New(l.T()) + testName := l.T().Name() + + svcClient, err := testcommon.GetServiceClient(l.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + ctx := context.Background() + fileName := testcommon.GenerateFileName(testName) + fileClient := shareClient.NewRootDirectoryClient().NewFileClient(fileName) + _, err = fileClient.Create(ctx, 0, nil) + _require.NoError(err) + + fileLeaseClient, err := lease.NewFileClient(fileClient, &lease.FileClientOptions{ + LeaseID: proposedLeaseIDs[0], + }) + _require.NoError(err) + + acquireLeaseResponse, err := fileLeaseClient.Acquire(ctx, nil) + _require.NoError(err) + _require.NotNil(acquireLeaseResponse.LeaseID) + _require.EqualValues(acquireLeaseResponse.LeaseID, fileLeaseClient.LeaseID()) + + _, err = fileClient.Delete(ctx, nil) + _require.Error(err) + + _, err = fileLeaseClient.Break(ctx, nil) + _require.NoError(err) + + _, err = fileClient.Delete(ctx, nil) + _require.NoError(err) +} diff --git a/sdk/storage/azfile/lease/file_client.go b/sdk/storage/azfile/lease/file_client.go index aa2a0c4b366d..b1bffc781a5b 100644 --- a/sdk/storage/azfile/lease/file_client.go +++ b/sdk/storage/azfile/lease/file_client.go @@ -8,9 +8,11 @@ package lease import ( "context" + "errors" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/base" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/generated" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/shared" ) // FileClient provides lease functionality for the underlying file client. @@ -29,7 +31,20 @@ type FileClientOptions struct { // - client - an instance of a file client // - options - client options; pass nil to accept the default values func NewFileClient(client *file.Client, options *FileClientOptions) (*FileClient, error) { - return nil, nil + var leaseID *string + if options != nil { + leaseID = options.LeaseID + } + + leaseID, err := shared.GenerateLeaseID(leaseID) + if err != nil { + return nil, err + } + + return &FileClient{ + fileClient: client, + leaseID: leaseID, + }, nil } func (f *FileClient) generated() *generated.FileClient { @@ -42,28 +57,47 @@ func (f *FileClient) LeaseID() *string { } // Acquire operation can be used to request a new lease. -// The lease duration must be between 15 and 60 seconds, or infinite (-1). // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-file. -func (f *FileClient) Acquire(ctx context.Context, duration int32, options *FileAcquireOptions) (FileAcquireResponse, error) { - // TODO: update generated code to make duration as required parameter - return FileAcquireResponse{}, nil +func (f *FileClient) Acquire(ctx context.Context, options *FileAcquireOptions) (FileAcquireResponse, error) { + opts := options.format(f.LeaseID()) + resp, err := f.generated().AcquireLease(ctx, (int32)(-1), opts) + return resp, err } // Break operation can be used to break the lease, if the file has an active lease. Once a lease is broken, it cannot be renewed. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-file. func (f *FileClient) Break(ctx context.Context, options *FileBreakOptions) (FileBreakResponse, error) { - return FileBreakResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := f.generated().BreakLease(ctx, opts, leaseAccessConditions) + return resp, err } // Change operation can be used to change the lease ID of an active lease. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-file. -func (f *FileClient) Change(ctx context.Context, leaseID string, proposedLeaseID string, options *FileChangeOptions) (FileChangeResponse, error) { - // TODO: update generated code to make proposedLeaseID as required parameter - return FileChangeResponse{}, nil +func (f *FileClient) Change(ctx context.Context, proposedLeaseID string, options *FileChangeOptions) (FileChangeResponse, error) { + if f.LeaseID() == nil { + return FileChangeResponse{}, errors.New("leaseID cannot be nil") + } + + opts := options.format(&proposedLeaseID) + resp, err := f.generated().ChangeLease(ctx, *f.LeaseID(), opts) + + // If lease has been changed successfully, set the leaseID in client + if err == nil { + f.leaseID = &proposedLeaseID + } + + return resp, err } // Release operation can be used to free the lease if it is no longer needed so that another client may immediately acquire a lease against the file. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-file. -func (f *FileClient) Release(ctx context.Context, leaseID string, options *FileReleaseOptions) (FileReleaseResponse, error) { - return FileReleaseResponse{}, nil +func (f *FileClient) Release(ctx context.Context, options *FileReleaseOptions) (FileReleaseResponse, error) { + if f.LeaseID() == nil { + return FileReleaseResponse{}, errors.New("leaseID cannot be nil") + } + + opts := options.format() + resp, err := f.generated().ReleaseLease(ctx, *f.LeaseID(), opts) + return resp, err } diff --git a/sdk/storage/azfile/lease/models.go b/sdk/storage/azfile/lease/models.go index cc9dd6e42eb7..e42ccc2b95b9 100644 --- a/sdk/storage/azfile/lease/models.go +++ b/sdk/storage/azfile/lease/models.go @@ -13,9 +13,13 @@ type AccessConditions = generated.LeaseAccessConditions // FileAcquireOptions contains the optional parameters for the FileClient.Acquire method. type FileAcquireOptions struct { - // Proposed lease ID, in a GUID string format. - // The File service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. - ProposedLeaseID *string + // placeholder for future options +} + +func (o *FileAcquireOptions) format(proposedLeaseID *string) *generated.FileClientAcquireLeaseOptions { + return &generated.FileClientAcquireLeaseOptions{ + ProposedLeaseID: proposedLeaseID, + } } // FileBreakOptions contains the optional parameters for the FileClient.Break method. @@ -24,31 +28,55 @@ type FileBreakOptions struct { AccessConditions *AccessConditions } +func (o *FileBreakOptions) format() (*generated.FileClientBreakLeaseOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + + return nil, o.AccessConditions +} + // FileChangeOptions contains the optional parameters for the FileClient.Change method. type FileChangeOptions struct { // placeholder for future options } +func (o *FileChangeOptions) format(proposedLeaseID *string) *generated.FileClientChangeLeaseOptions { + return &generated.FileClientChangeLeaseOptions{ + ProposedLeaseID: proposedLeaseID, + } +} + // FileReleaseOptions contains the optional parameters for the FileClient.Release method. type FileReleaseOptions struct { // placeholder for future options } +func (o *FileReleaseOptions) format() *generated.FileClientReleaseLeaseOptions { + return nil +} + // --------------------------------------------------------------------------------------------------------------------- // ShareAcquireOptions contains the optional parameters for the ShareClient.Acquire method. type ShareAcquireOptions struct { - // Proposed lease ID, in a GUID string format. - // The File service returns 400 (Invalid request) if the proposed lease ID is not in the correct format. - ProposedLeaseID *string - // TODO: Should snapshot be removed from the option bag // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. ShareSnapshot *string } +func (o *ShareAcquireOptions) format(proposedLeaseID *string) *generated.ShareClientAcquireLeaseOptions { + opts := &generated.ShareClientAcquireLeaseOptions{ + ProposedLeaseID: proposedLeaseID, + } + if o != nil { + opts.Sharesnapshot = o.ShareSnapshot + } + return opts +} + // ShareBreakOptions contains the optional parameters for the ShareClient.Break method. type ShareBreakOptions struct { - // For a break operation, proposed duration the lease should continue before it is broken, in seconds, between 0 and 60. This + // For a break operation, this is the proposed duration the lease should continue before it is broken, in seconds, between 0 and 60. This // break period is only used if it is shorter than the time remaining on the // lease. If longer, the time remaining on the lease is used. A new lease will not be available before the break period has // expired, but the lease may be held for longer than the break period. If this @@ -62,23 +90,59 @@ type ShareBreakOptions struct { AccessConditions *AccessConditions } +func (o *ShareBreakOptions) format() (*generated.ShareClientBreakLeaseOptions, *generated.LeaseAccessConditions) { + if o == nil { + return nil, nil + } + + return &generated.ShareClientBreakLeaseOptions{ + BreakPeriod: o.BreakPeriod, + Sharesnapshot: o.ShareSnapshot, + }, o.AccessConditions +} + // ShareChangeOptions contains the optional parameters for the ShareClient.Change method. type ShareChangeOptions struct { - // TODO: Should snapshot be removed from the option bag // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. ShareSnapshot *string } +func (o *ShareChangeOptions) format(proposedLeaseID *string) *generated.ShareClientChangeLeaseOptions { + opts := &generated.ShareClientChangeLeaseOptions{ + ProposedLeaseID: proposedLeaseID, + } + if o != nil { + opts.Sharesnapshot = o.ShareSnapshot + } + return opts +} + // ShareReleaseOptions contains the optional parameters for the ShareClient.Release method. type ShareReleaseOptions struct { - // TODO: Should snapshot be removed from the option bag // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. ShareSnapshot *string } +func (o *ShareReleaseOptions) format() *generated.ShareClientReleaseLeaseOptions { + if o == nil { + return nil + } + return &generated.ShareClientReleaseLeaseOptions{ + Sharesnapshot: o.ShareSnapshot, + } +} + // ShareRenewOptions contains the optional parameters for the ShareClient.Renew method. type ShareRenewOptions struct { - // TODO: Should snapshot be removed from the option bag // The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. ShareSnapshot *string } + +func (o *ShareRenewOptions) format() *generated.ShareClientRenewLeaseOptions { + if o == nil { + return nil + } + return &generated.ShareClientRenewLeaseOptions{ + Sharesnapshot: o.ShareSnapshot, + } +} diff --git a/sdk/storage/azfile/lease/share_client.go b/sdk/storage/azfile/lease/share_client.go index 6b11c0da1582..ff4db564c57f 100644 --- a/sdk/storage/azfile/lease/share_client.go +++ b/sdk/storage/azfile/lease/share_client.go @@ -8,8 +8,10 @@ package lease import ( "context" + "errors" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/base" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/generated" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/internal/shared" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/share" ) @@ -29,7 +31,20 @@ type ShareClientOptions struct { // - client - an instance of a share client // - options - client options; pass nil to accept the default values func NewShareClient(client *share.Client, options *ShareClientOptions) (*ShareClient, error) { - return nil, nil + var leaseID *string + if options != nil { + leaseID = options.LeaseID + } + + leaseID, err := shared.GenerateLeaseID(leaseID) + if err != nil { + return nil, err + } + + return &ShareClient{ + shareClient: client, + leaseID: leaseID, + }, nil } func (s *ShareClient) generated() *generated.ShareClient { @@ -45,31 +60,57 @@ func (s *ShareClient) LeaseID() *string { // The lease duration must be between 15 and 60 seconds, or infinite (-1). // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-share. func (s *ShareClient) Acquire(ctx context.Context, duration int32, options *ShareAcquireOptions) (ShareAcquireResponse, error) { - // TODO: update generated code to make duration as required parameter - return ShareAcquireResponse{}, nil + opts := options.format(s.LeaseID()) + resp, err := s.generated().AcquireLease(ctx, duration, opts) + return resp, err } // Break operation can be used to break the lease, if the file share has an active lease. Once a lease is broken, it cannot be renewed. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-share. func (s *ShareClient) Break(ctx context.Context, options *ShareBreakOptions) (ShareBreakResponse, error) { - return ShareBreakResponse{}, nil + opts, leaseAccessConditions := options.format() + resp, err := s.generated().BreakLease(ctx, opts, leaseAccessConditions) + return resp, err } // Change operation can be used to change the lease ID of an active lease. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-share. -func (s *ShareClient) Change(ctx context.Context, leaseID string, proposedLeaseID string, options *ShareChangeOptions) (ShareChangeResponse, error) { - // TODO: update generated code to make proposedLeaseID as required parameter - return ShareChangeResponse{}, nil +func (s *ShareClient) Change(ctx context.Context, proposedLeaseID string, options *ShareChangeOptions) (ShareChangeResponse, error) { + if s.LeaseID() == nil { + return ShareChangeResponse{}, errors.New("leaseID cannot be nil") + } + + opts := options.format(&proposedLeaseID) + resp, err := s.generated().ChangeLease(ctx, *s.LeaseID(), opts) + + // If lease has been changed successfully, set the leaseID in client + if err == nil { + s.leaseID = &proposedLeaseID + } + + return resp, err } // Release operation can be used to free the lease if it is no longer needed so that another client may immediately acquire a lease against the file share. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-share. -func (s *ShareClient) Release(ctx context.Context, leaseID string, options *ShareReleaseOptions) (ShareReleaseResponse, error) { - return ShareReleaseResponse{}, nil +func (s *ShareClient) Release(ctx context.Context, options *ShareReleaseOptions) (ShareReleaseResponse, error) { + if s.LeaseID() == nil { + return ShareReleaseResponse{}, errors.New("leaseID cannot be nil") + } + + opts := options.format() + resp, err := s.generated().ReleaseLease(ctx, *s.LeaseID(), opts) + return resp, err } // Renew operation can be used to renew an existing lease. // For more information, see https://learn.microsoft.com/en-us/rest/api/storageservices/lease-share. -func (s *ShareClient) Renew(ctx context.Context, leaseID string, options *ShareRenewOptions) (ShareRenewResponse, error) { - return ShareRenewResponse{}, nil +func (s *ShareClient) Renew(ctx context.Context, options *ShareRenewOptions) (ShareRenewResponse, error) { + if s.LeaseID() == nil { + return ShareRenewResponse{}, errors.New("leaseID cannot be nil") + } + + opts := options.format() + resp, err := s.generated().RenewLease(ctx, *s.LeaseID(), opts) + return resp, err } diff --git a/sdk/storage/azfile/service/client_test.go b/sdk/storage/azfile/service/client_test.go index 3ca8732945e1..236da07ba3f7 100644 --- a/sdk/storage/azfile/service/client_test.go +++ b/sdk/storage/azfile/service/client_test.go @@ -60,11 +60,11 @@ type ServiceUnrecordedTestsSuite struct { suite.Suite } -func (s *ServiceUnrecordedTestsSuite) TestAccountNewServiceURLValidName() { +func (s *ServiceRecordedTestsSuite) TestAccountNewServiceURLValidName() { _require := require.New(s.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -73,12 +73,12 @@ func (s *ServiceUnrecordedTestsSuite) TestAccountNewServiceURLValidName() { _require.Equal(svcClient.URL(), correctURL) } -func (s *ServiceUnrecordedTestsSuite) TestAccountNewShareURLValidName() { +func (s *ServiceRecordedTestsSuite) TestAccountNewShareURLValidName() { _require := require.New(s.T()) testName := s.T().Name() - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -91,7 +91,7 @@ func (s *ServiceUnrecordedTestsSuite) TestAccountNewShareURLValidName() { _require.Equal(shareClient.URL(), correctURL) } -func (s *ServiceUnrecordedTestsSuite) TestServiceClientFromConnectionString() { +func (s *ServiceRecordedTestsSuite) TestServiceClientFromConnectionString() { _require := require.New(s.T()) svcClient, err := testcommon.GetServiceClientFromConnectionString(s.T(), testcommon.TestAccountDefault, nil) @@ -102,7 +102,7 @@ func (s *ServiceUnrecordedTestsSuite) TestServiceClientFromConnectionString() { _require.NotNil(resp.RequestID) } -func (s *ServiceUnrecordedTestsSuite) TestAccountProperties() { +func (s *ServiceRecordedTestsSuite) TestAccountProperties() { _require := require.New(s.T()) svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -172,7 +172,7 @@ func (s *ServiceRecordedTestsSuite) TestAccountHourMetrics() { _require.NoError(err) } -func (s *ServiceUnrecordedTestsSuite) TestAccountListSharesNonDefault() { +func (s *ServiceRecordedTestsSuite) TestAccountListSharesNonDefault() { _require := require.New(s.T()) testName := s.T().Name() @@ -316,10 +316,10 @@ func (s *ServiceUnrecordedTestsSuite) TestSASServiceClientRestoreShare() { _require.Equal(sharesCnt, 1) } -func (s *ServiceUnrecordedTestsSuite) TestSASServiceClientNoKey() { +func (s *ServiceRecordedTestsSuite) TestSASServiceClientNoKey() { _require := require.New(s.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) serviceClient, err := service.NewClientWithNoCredential(fmt.Sprintf("https://%s.file.core.windows.net/", accountName), nil) _require.NoError(err) @@ -341,12 +341,11 @@ func (s *ServiceUnrecordedTestsSuite) TestSASServiceClientNoKey() { _require.Equal(err, fileerror.MissingSharedKeyCredential) } -func (s *ServiceUnrecordedTestsSuite) TestSASServiceClientSignNegative() { +func (s *ServiceRecordedTestsSuite) TestSASServiceClientSignNegative() { _require := require.New(s.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) - accountKey, err := testcommon.GetRequiredEnv(testcommon.AccountKeyEnvVar) - _require.NoError(err) + accountName, accountKey := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + _require.Greater(len(accountKey), 0) cred, err := service.NewSharedKeyCredential(accountName, accountKey) _require.NoError(err) diff --git a/sdk/storage/azfile/share/client_test.go b/sdk/storage/azfile/share/client_test.go index 95f23a11d0a0..80574dd3cbba 100644 --- a/sdk/storage/azfile/share/client_test.go +++ b/sdk/storage/azfile/share/client_test.go @@ -60,7 +60,7 @@ type ShareUnrecordedTestsSuite struct { suite.Suite } -func (s *ShareUnrecordedTestsSuite) TestShareCreateRootDirectoryURL() { +func (s *ShareRecordedTestsSuite) TestShareCreateRootDirectoryURL() { _require := require.New(s.T()) testName := s.T().Name() @@ -74,12 +74,12 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateRootDirectoryURL() { _require.Equal(shareClient.URL(), rootDirClient.URL()) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateDirectoryURL() { +func (s *ShareRecordedTestsSuite) TestShareCreateDirectoryURL() { _require := require.New(s.T()) testName := s.T().Name() - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -94,7 +94,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateDirectoryURL() { _require.Equal(dirClient.URL(), correctURL) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateUsingSharedKey() { +func (s *ShareRecordedTestsSuite) TestShareCreateUsingSharedKey() { _require := require.New(s.T()) testName := s.T().Name() @@ -103,7 +103,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateUsingSharedKey() { shareName := testcommon.GenerateShareName(testName) shareURL := "https://" + cred.AccountName() + ".file.core.windows.net/" + shareName - shareClient, err := share.NewClientWithSharedKeyCredential(shareURL, cred, nil) + options := &share.ClientOptions{} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) + shareClient, err := share.NewClientWithSharedKeyCredential(shareURL, cred, options) _require.NoError(err) resp, err := shareClient.Create(context.Background(), nil) @@ -114,7 +116,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateUsingSharedKey() { _require.NotNil(resp.RequestID) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateUsingConnectionString() { +func (s *ShareRecordedTestsSuite) TestShareCreateUsingConnectionString() { _require := require.New(s.T()) testName := s.T().Name() @@ -122,7 +124,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateUsingConnectionString() { _require.NoError(err) shareName := testcommon.GenerateShareName(testName) - shareClient, err := share.NewClientFromConnectionString(*connString, shareName, nil) + options := &share.ClientOptions{} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) + shareClient, err := share.NewClientFromConnectionString(*connString, shareName, options) _require.NoError(err) resp, err := shareClient.Create(context.Background(), nil) @@ -163,7 +167,6 @@ func (s *ShareUnrecordedTestsSuite) TestShareClientUsingSAS() { _require.Error(err) testcommon.ValidateFileErrorCode(_require, err, fileerror.AuthorizationFailure) - // TODO: create files using shareSASClient dirName1 := testcommon.GenerateDirectoryName(testName) + "1" _, err = shareSASClient.NewDirectoryClient(dirName1).Create(context.Background(), nil) _require.NoError(err) @@ -172,6 +175,14 @@ func (s *ShareUnrecordedTestsSuite) TestShareClientUsingSAS() { _, err = shareSASClient.NewDirectoryClient(dirName2).Create(context.Background(), nil) _require.NoError(err) + fileName1 := testcommon.GenerateFileName(testName) + "1" + _, err = shareSASClient.NewRootDirectoryClient().NewFileClient(fileName1).Create(context.Background(), 1024, nil) + _require.NoError(err) + + fileName2 := testcommon.GenerateFileName(testName) + "2" + _, err = shareSASClient.NewDirectoryClient(dirName2).NewFileClient(fileName2).Create(context.Background(), 1024, nil) + _require.NoError(err) + dirCtr, fileCtr := 0, 0 pager := shareSASClient.NewRootDirectoryClient().NewListFilesAndDirectoriesPager(nil) for pager.More() { @@ -181,10 +192,10 @@ func (s *ShareUnrecordedTestsSuite) TestShareClientUsingSAS() { fileCtr += len(resp.Segment.Files) } _require.Equal(dirCtr, 2) - _require.Equal(fileCtr, 0) + _require.Equal(fileCtr, 1) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateDeleteNonDefault() { +func (s *ShareRecordedTestsSuite) TestShareCreateDeleteNonDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -240,7 +251,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateDeleteNonDefault() { } } -func (s *ShareUnrecordedTestsSuite) TestShareCreateNilMetadata() { +func (s *ShareRecordedTestsSuite) TestShareCreateNilMetadata() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -258,7 +269,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateNilMetadata() { _require.Len(response.Metadata, 0) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateNegativeInvalidName() { +func (s *ShareRecordedTestsSuite) TestShareCreateNegativeInvalidName() { _require := require.New(s.T()) svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) @@ -270,7 +281,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateNegativeInvalidName() { testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidResourceName) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateNegativeInvalidMetadata() { +func (s *ShareRecordedTestsSuite) TestShareCreateNegativeInvalidMetadata() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -286,7 +297,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateNegativeInvalidMetadata() { _require.Error(err) } -func (s *ShareUnrecordedTestsSuite) TestShareDeleteNegativeNonExistent() { +func (s *ShareRecordedTestsSuite) TestShareDeleteNegativeNonExistent() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -299,7 +310,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareDeleteNegativeNonExistent() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareNotFound) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetPropertiesNonDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetPropertiesNonDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -333,7 +344,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetPropertiesNonDefault() { _require.Equal(*props.AccessTier, string(share.AccessTierHot)) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetPropertiesDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetPropertiesDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -361,7 +372,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetPropertiesDefault() { _require.Greater(*props.Quota, int32(0)) // When using service default quota, it could be any value } -func (s *ShareUnrecordedTestsSuite) TestShareSetQuotaNegative() { +func (s *ShareRecordedTestsSuite) TestShareSetQuotaNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -376,7 +387,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetQuotaNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidHeaderValue) } -func (s *ShareUnrecordedTestsSuite) TestShareGetPropertiesNegative() { +func (s *ShareRecordedTestsSuite) TestShareGetPropertiesNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -390,7 +401,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetPropertiesNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareNotFound) } -func (s *ShareUnrecordedTestsSuite) TestSharePutAndGetPermission() { +func (s *ShareRecordedTestsSuite) TestSharePutAndGetPermission() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -417,7 +428,7 @@ func (s *ShareUnrecordedTestsSuite) TestSharePutAndGetPermission() { _require.NotEmpty(*getResp.Permission) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetAccessPolicyNonDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -427,7 +438,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefault() { shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - now := time.Now().UTC().Truncate(10000 * time.Millisecond) // Enough resolution + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + now := currTime.UTC().Truncate(10000 * time.Millisecond) // Enough resolution expiryTIme := now.Add(5 * time.Minute).UTC() pS := share.AccessPolicyPermission{ Read: true, @@ -473,7 +486,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefault() { _require.EqualValues(*(gResp.SignedIdentifiers[0]), *permissions[0]) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefaultMultiple() { +func (s *ShareRecordedTestsSuite) TestShareGetSetAccessPolicyNonDefaultMultiple() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -483,7 +496,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefaultMultipl shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - now := time.Now().UTC().Truncate(10000 * time.Millisecond) // Enough resolution + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + now := currTime.UTC().Truncate(10000 * time.Millisecond) // Enough resolution expiryTIme := now.Add(5 * time.Minute).UTC() permission := share.AccessPolicyPermission{ Read: true, @@ -530,7 +545,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyNonDefaultMultipl _require.EqualValues(gResp.SignedIdentifiers[1], permissions[1]) } -func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyMoreThanFive() { +func (s *ShareRecordedTestsSuite) TestShareSetAccessPolicyMoreThanFive() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -540,7 +555,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyMoreThanFive() { shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - now := time.Now().UTC().Truncate(10000 * time.Millisecond) // Enough resolution + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + now := currTime.UTC().Truncate(10000 * time.Millisecond) // Enough resolution expiryTIme := now.Add(5 * time.Minute).UTC() permission := share.AccessPolicyPermission{ Read: true, @@ -574,7 +591,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyMoreThanFive() { testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidXMLDocument) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetAccessPolicyDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -602,7 +619,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetAccessPolicyDefault() { _require.Len(gResp.SignedIdentifiers, 0) } -func (s *ShareUnrecordedTestsSuite) TestShareGetAccessPolicyNegative() { +func (s *ShareRecordedTestsSuite) TestShareGetAccessPolicyNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -616,7 +633,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetAccessPolicyNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareNotFound) } -func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyNonDefaultDeleteAndModifyACL() { +func (s *ShareRecordedTestsSuite) TestShareSetAccessPolicyNonDefaultDeleteAndModifyACL() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -626,7 +643,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyNonDefaultDeleteAndM shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - start := time.Now().UTC().Truncate(10000 * time.Millisecond) + currTime, err := time.Parse(time.UnixDate, "Thu Mar 30 20:00:00 GMT 2023") + _require.NoError(err) + start := currTime.UTC().Truncate(10000 * time.Millisecond) expiry := start.Add(5 * time.Minute).UTC() accessPermission := share.AccessPolicyPermission{List: true}.String() permissions := make([]*share.SignedIdentifier, 2) @@ -664,7 +683,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyNonDefaultDeleteAndM _require.EqualValues(resp.SignedIdentifiers, permissions) } -func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyDeleteAllPolicies() { +func (s *ShareRecordedTestsSuite) TestShareSetAccessPolicyDeleteAllPolicies() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -674,7 +693,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyDeleteAllPolicies() shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer testcommon.DeleteShare(context.Background(), _require, shareClient) - start := time.Now().UTC() + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + start := currTime.UTC() expiry := start.Add(5 * time.Minute).UTC() accessPermission := share.AccessPolicyPermission{List: true}.String() permissions := make([]*share.SignedIdentifier, 2) @@ -706,7 +727,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetAccessPolicyDeleteAllPolicies() _require.Len(resp2.SignedIdentifiers, 0) } -func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegativeInvalidPolicyTimes() { +func (s *ShareRecordedTestsSuite) TestShareSetPermissionsNegativeInvalidPolicyTimes() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -717,7 +738,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegativeInvalidPolicy defer testcommon.DeleteShare(context.Background(), _require, shareClient) // Swap start and expiry - expiry := time.Now().UTC() + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + expiry := currTime.UTC() start := expiry.Add(5 * time.Minute).UTC() accessPermission := share.AccessPolicyPermission{List: true}.String() permissions := make([]*share.SignedIdentifier, 2) @@ -744,7 +767,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegativeInvalidPolicy } // SignedIdentifier ID too long -func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegative() { +func (s *ShareRecordedTestsSuite) TestShareSetPermissionsNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -758,7 +781,9 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegative() { for i := 0; i < 65; i++ { id += "a" } - expiry := time.Now().UTC() + currTime, err := time.Parse(time.UnixDate, "Wed Mar 29 20:00:00 GMT 2023") + _require.NoError(err) + expiry := currTime.UTC() start := expiry.Add(5 * time.Minute).UTC() accessPermission := share.AccessPolicyPermission{List: true}.String() permissions := make([]*share.SignedIdentifier, 2) @@ -780,7 +805,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetPermissionsNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.InvalidXMLDocument) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetMetadataDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetMetadataDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -810,7 +835,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetMetadataDefault() { _require.Len(gResp.Metadata, 0) } -func (s *ShareUnrecordedTestsSuite) TestShareGetSetMetadataNonDefault() { +func (s *ShareRecordedTestsSuite) TestShareGetSetMetadataNonDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -844,7 +869,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetSetMetadataNonDefault() { _require.EqualValues(gResp.Metadata, md) } -func (s *ShareUnrecordedTestsSuite) TestShareSetMetadataNegative() { +func (s *ShareRecordedTestsSuite) TestShareSetMetadataNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -863,7 +888,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareSetMetadataNegative() { _require.Error(err) } -func (s *ShareUnrecordedTestsSuite) TestShareGetStats() { +func (s *ShareRecordedTestsSuite) TestShareGetStats() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -886,10 +911,10 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetStats() { // _require.Equal(gResp.LastModified.IsZero(), false) // TODO: Even share is once updated, no LastModified would be returned. _require.NotNil(gResp.RequestID) _require.NotNil(gResp.Version) - _require.Equal(*gResp.ShareUsageBytes, int32(0)) + _require.Equal(*gResp.ShareUsageBytes, int64(0)) } -func (s *ShareUnrecordedTestsSuite) TestShareGetStatsNegative() { +func (s *ShareRecordedTestsSuite) TestShareGetStatsNegative() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -903,8 +928,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetStatsNegative() { testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareNotFound) } -// TODO: uncomment this test after directory and file clients are added -/*func (s *ShareUnrecordedTestsSuite) TestSetAndGetStatistics() { +func (s *ShareRecordedTestsSuite) TestSetAndGetStatistics() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -922,20 +946,20 @@ func (s *ShareUnrecordedTestsSuite) TestShareGetStatsNegative() { _require.NoError(err) fileClient := dirClient.NewFileClient("testfile") - _, err = fileClient.Create(context.Background(), int64(1024 * 1024 * 1024 * 1024), nil) + _, err = fileClient.Create(context.Background(), int64(1024*1024*1024*1024), nil) _require.NoError(err) getStats, err := shareClient.GetStatistics(context.Background(), nil) _require.NoError(err) _require.Equal(*getStats.ShareUsageBytes, int64(1024*1024*1024*1024)) -}*/ +} func deleteShare(ctx context.Context, _require *require.Assertions, shareClient *share.Client, o *share.DeleteOptions) { _, err := shareClient.Delete(ctx, o) _require.NoError(err) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNonDefault() { +func (s *ShareRecordedTestsSuite) TestShareCreateSnapshotNonDefault() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -983,22 +1007,96 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNonDefault() { _require.True(foundSnapshot) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeShareNotExist() { +func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotDefault() { _require := require.New(s.T()) testName := s.T().Name() + + cred, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault) + _require.NoError(err) + svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) _require.NoError(err) shareName := testcommon.GenerateShareName(testName) - shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + shareClient := svcClient.NewShareClient(shareName) + + _, err = shareClient.Create(context.Background(), nil) + _require.NoError(err) defer deleteShare(context.Background(), _require, shareClient, &share.DeleteOptions{DeleteSnapshots: to.Ptr(share.DeleteSnapshotsOptionTypeInclude)}) + // create a file in the base share. + dirClient := shareClient.NewRootDirectoryClient() + _require.NoError(err) + + fClient := dirClient.NewFileClient("myfile") + _, err = fClient.Create(context.Background(), 0, nil) + _require.NoError(err) + + // Create share snapshot, the snapshot contains the create file. + snapshotShare, err := shareClient.CreateSnapshot(context.Background(), nil) + _require.NoError(err) + + // Delete file in base share. + _, err = fClient.Delete(context.Background(), nil) + _require.NoError(err) + + // To produce a share SAS (as opposed to a file SAS), assign to FilePermissions using + // ShareSASPermissions and make sure the DirectoryAndFilePath field is "" (the default). + perms := sas.SharePermissions{Read: true, Write: true} + + // Restore file from share snapshot. + // Create a SAS. + sasQueryParams, err := sas.SignatureValues{ + Protocol: sas.ProtocolHTTPS, // Users MUST use HTTPS (not HTTP) + ExpiryTime: time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration + ShareName: shareName, + Permissions: perms.String(), + }.SignWithSharedKey(cred) + _require.NoError(err) + + // Build a file snapshot URL. + fileParts, err := sas.ParseURL(fClient.URL()) + _require.NoError(err) + fileParts.ShareSnapshot = *snapshotShare.Snapshot + fileParts.SAS = sasQueryParams + sourceURL := fileParts.String() + + // Before restore + _, err = fClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) + + // Do restore. + _, err = fClient.StartCopyFromURL(context.Background(), sourceURL, nil) + _require.NoError(err) + + time.Sleep(2 * time.Second) + + // After restore + _, err = fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + + _, err = shareClient.Delete(context.Background(), &share.DeleteOptions{ + ShareSnapshot: snapshotShare.Snapshot, + }) + _require.NoError(err) +} + +func (s *ShareRecordedTestsSuite) TestShareCreateSnapshotNegativeShareNotExist() { + _require := require.New(s.T()) + testName := s.T().Name() + svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.GetShareClient(shareName, svcClient) + _, err = shareClient.CreateSnapshot(context.Background(), &share.CreateSnapshotOptions{Metadata: map[string]*string{}}) _require.Error(err) testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareNotFound) } -func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshot() { +func (s *ShareRecordedTestsSuite) TestShareDeleteSnapshot() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -1064,7 +1162,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshot() { _require.Equal(snapshotsCtr, 1) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeMetadataInvalid() { +func (s *ShareRecordedTestsSuite) TestShareCreateSnapshotNegativeMetadataInvalid() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -1078,7 +1176,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeMetadataInval _require.Error(err) } -func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeSnapshotOfSnapshot() { +func (s *ShareRecordedTestsSuite) TestShareCreateSnapshotNegativeSnapshotOfSnapshot() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -1088,7 +1186,10 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeSnapshotOfSna shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) defer deleteShare(context.Background(), _require, shareClient, &share.DeleteOptions{DeleteSnapshots: to.Ptr(share.DeleteSnapshotsOptionTypeInclude)}) - snapshotClient, err := shareClient.WithSnapshot(time.Now().UTC().String()) + snapTime, err := time.Parse(time.UnixDate, "Fri Mar 31 20:00:00 GMT 2023") + _require.NoError(err) + + snapshotClient, err := shareClient.WithSnapshot(snapTime.UTC().String()) _require.NoError(err) cResp, err := snapshotClient.CreateSnapshot(context.Background(), nil) @@ -1102,7 +1203,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareCreateSnapshotNegativeSnapshotOfSna _require.NoError(err) //Note: this would not fail, snapshot would be ignored. } -func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshotsInclude() { +func (s *ShareRecordedTestsSuite) TestShareDeleteSnapshotsInclude() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -1140,7 +1241,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshotsInclude() { } } -func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshotsNoneWithSnapshots() { +func (s *ShareRecordedTestsSuite) TestShareDeleteSnapshotsNoneWithSnapshots() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) @@ -1158,7 +1259,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareDeleteSnapshotsNoneWithSnapshots() testcommon.ValidateFileErrorCode(_require, err, fileerror.ShareHasSnapshots) } -func (s *ShareUnrecordedTestsSuite) TestShareRestore() { +func (s *ShareRecordedTestsSuite) TestShareRestoreSuccess() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountSoftDelete, nil) @@ -1215,7 +1316,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareRestore() { _require.Equal(shareCtr, 1) } -func (s *ShareUnrecordedTestsSuite) TestShareRestoreFailures() { +func (s *ShareRecordedTestsSuite) TestShareRestoreFailures() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountSoftDelete, nil) @@ -1230,7 +1331,7 @@ func (s *ShareUnrecordedTestsSuite) TestShareRestoreFailures() { testcommon.ValidateFileErrorCode(_require, err, fileerror.MissingRequiredHeader) } -func (s *ShareUnrecordedTestsSuite) TestShareRestoreWithSnapshotsAgain() { +func (s *ShareRecordedTestsSuite) TestShareRestoreWithSnapshotsAgain() { _require := require.New(s.T()) testName := s.T().Name() svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountSoftDelete, nil) @@ -1299,10 +1400,10 @@ func (s *ShareUnrecordedTestsSuite) TestShareRestoreWithSnapshotsAgain() { _require.Equal(shareCtr, 2) // 1 share and 1 snapshot } -func (s *ShareUnrecordedTestsSuite) TestSASShareClientNoKey() { +func (s *ShareRecordedTestsSuite) TestSASShareClientNoKey() { _require := require.New(s.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) testName := s.T().Name() shareName := testcommon.GenerateShareName(testName) @@ -1322,12 +1423,11 @@ func (s *ShareUnrecordedTestsSuite) TestSASShareClientNoKey() { _require.Equal(err, fileerror.MissingSharedKeyCredential) } -func (s *ShareUnrecordedTestsSuite) TestSASShareClientSignNegative() { +func (s *ShareRecordedTestsSuite) TestSASShareClientSignNegative() { _require := require.New(s.T()) - accountName, err := testcommon.GetRequiredEnv(testcommon.AccountNameEnvVar) - _require.NoError(err) - accountKey, err := testcommon.GetRequiredEnv(testcommon.AccountKeyEnvVar) - _require.NoError(err) + accountName, accountKey := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + _require.Greater(len(accountKey), 0) cred, err := service.NewSharedKeyCredential(accountName, accountKey) _require.NoError(err)