From 757507a034b401c0f9fe65efc6df83e3d3b92be1 Mon Sep 17 00:00:00 2001 From: Tamer Sherif <69483382+tasherif-msft@users.noreply.github.com> Date: Mon, 31 Jul 2023 09:13:29 -0700 Subject: [PATCH] [AzDatalake] Directory Client Implementation (#21283) * Enable gocritic during linting (#20715) Enabled gocritic's evalOrder to catch dependencies on undefined behavior on return statements. Updated to latest version of golangci-lint. Fixed issue in azblob flagged by latest linter. * Cosmos DB: Enable merge support (#20716) * Adding header and value * Wiring and tests * format * Fixing value * change log * [azservicebus, azeventhubs] Stress test and logging improvement (#20710) Logging improvements: * Updating the logging to print more tracing information (per-link) in prep for the bigger release coming up. * Trimming out some of the verbose logging, seeing if I can get it a bit more reasonable. Stress tests: * Add a timestamp to the log name we generate and also default to append, not overwrite. * Use 0.5 cores, 0.5GB as our baseline. Some pods use more and I'll tune them more later. * update proxy version (#20712) Co-authored-by: Scott Beddall * Return an error when you try to send a message that's too large. (#20721) This now works just like the message batch - you'll get an ErrMessageTooLarge if you attempt to send a message that's too large for the link's configured size. NOTE: there's a patch to `internal/go-amqp/Sender.go` to match what's in go-amqp's main so it returns a programmatically useful error when the message is too large. Fixes #20647 * Changes in test that is failing in pipeline (#20693) * [azservicebus, azeventhubs] Treat 'entity full' as a fatal error (#20722) When the remote entity is full we get a resource-limit-exceeded condition. This isn't something we should keep retrying on and it's best to just abort and let the user know immediately, rather than hoping it might eventually clear out. This affected both Event Hubs and Service Bus. Fixes #20647 * [azservicebus/azeventhubs] Redirect stderr and stdout to tee (#20726) * Update changelog with latest features (#20730) * Update changelog with latest features Prepare for upcoming release. * bump minor version * pass along the artifact name so we can override it later (#20732) Co-authored-by: scbedd <45376673+scbedd@users.noreply.github.com> * [azeventhubs] Fixing checkpoint store race condition (#20727) The checkpoint store wasn't guarding against multiple owners claiming for the first time - fixing this by using IfNoneMatch Fixes #20717 * Fix azidentity troubleshooting guide link (#20736) * [Release] sdk/resourcemanager/paloaltonetworksngfw/armpanngfw/0.1.0 (#20437) * [Release] sdk/resourcemanager/paloaltonetworksngfw/armpanngfw/0.1.0 generation from spec commit: 85fb4ac6f8bfefd179e6c2632976a154b5c9ff04 * client factory * fix * fix * update * add sdk/resourcemanager/postgresql/armpostgresql live test (#20685) * add sdk/resourcemanager/postgresql/armpostgresql live test * update assets.json * set subscriptionId default value * format * add sdk/resourcemanager/eventhub/armeventhub live test (#20686) * add sdk/resourcemanager/eventhub/armeventhub live test * update assets * add sdk/resourcemanager/compute/armcompute live test (#20048) * add sdk/resourcemanager/compute/armcompute live test * skus filter * fix subscriptionId default value * fix * gofmt * update recording * sdk/resourcemanager/network/armnetwork live test (#20331) * sdk/resourcemanager/network/armnetwork live test * update subscriptionId default value * update recording * add sdk/resourcemanager/cosmos/armcosmos live test (#20705) * add sdk/resourcemanager/cosmos/armcosmos live test * update assets.json * update assets.json * update assets.json * update assets.json * Increment package version after release of azcore (#20740) * [azeventhubs] Improperly resetting etag in the checkpoint store (#20737) We shouldn't be resetting the etag to nil - it's what we use to enforce a "single winner" when doing ownership claims. The bug here was two-fold: I had bad logic in my previous claim ownership, which I fixed in a previous PR, but we need to reflect that same constraint properly in our in-memory checkpoint store for these tests. * Eng workflows sync and branch cleanup additions (#20743) Co-authored-by: James Suplizio * [azeventhubs] Latest start position can also be inclusive (ie, get the latest message) (#20744) * Update GitHubEventProcessor version and remove pull_request_review procesing (#20751) Co-authored-by: James Suplizio * Rename DisableAuthorityValidationAndInstanceDiscovery (#20746) * fix (#20707) * AzFile (#20739) * azfile: Fixing connection string parsing logic (#20798) * Fixing connection string parse logic * Update README * [azadmin] fix flaky test (#20758) * fix flaky test * charles suggestion * Prepare azidentity v1.3.0 for release (#20756) * Fix broken podman link (#20801) Co-authored-by: Wes Haggard * [azquery] update doc comments (#20755) * update doc comments * update statistics and visualization generation * prep-for-release * Fixed contribution section (#20752) Co-authored-by: Bob Tabor * [azeventhubs,azservicebus] Some API cleanup, renames (#20754) * Adding options to UpdateCheckpoint(), just for future potential expansion * Make Offset an int64, not a *int64 (it's not optional, it'll always come back with ReceivedEvents) * Adding more logging into the checkpoint store. * Point all imports at the production go-amqp * Add supporting features to enable distributed tracing (#20301) (#20708) * Add supporting features to enable distributed tracing This includes new internal pipeline policies and other supporting types. See the changelog for a full description. Added some missing doc comments. * fix linter issue * add net.peer.name trace attribute sequence custom HTTP header policy before logging policy. sequence logging policy after HTTP trace policy. keep body download policy at the end. * add span for iterating over pages * Restore ARM CAE support for azcore beta (#20657) This reverts commit 902097226ff3fe2fc6c3e7fc50d3478350253614. * Upgrade to stable azcore (#20808) * Increment package version after release of data/azcosmos (#20807) * Updating changelog (#20810) * Add fake package to azcore (#20711) * Add fake package to azcore This is the supporting infrastructure for the generated SDK fakes. * fix doc comment * Updating CHANGELOG.md (#20809) * changelog (#20811) * Increment package version after release of storage/azfile (#20813) * Update changelog (azblob) (#20815) * Updating CHANGELOG.md * Update the changelog with correct version * [azquery] migration guide (#20742) * migration guide * Charles feedback * Richard feedback --------- Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com> * Increment package version after release of monitor/azquery (#20820) * [keyvault] prep for release (#20819) * prep for release * perf tests * update date * added dir methods * small fixes * added rescursive set acl methods * recursive support * added sas and tests --------- Co-authored-by: Joel Hendrix Co-authored-by: Matias Quaranta Co-authored-by: Richard Park <51494936+richardpark-msft@users.noreply.github.com> Co-authored-by: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Co-authored-by: Scott Beddall Co-authored-by: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Co-authored-by: scbedd <45376673+scbedd@users.noreply.github.com> Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com> Co-authored-by: Peng Jiahui <46921893+Alancere@users.noreply.github.com> Co-authored-by: James Suplizio Co-authored-by: Sourav Gupta <98318303+souravgupta-msft@users.noreply.github.com> Co-authored-by: gracewilcox <43627800+gracewilcox@users.noreply.github.com> Co-authored-by: Wes Haggard Co-authored-by: Bob Tabor Co-authored-by: Bob Tabor --- sdk/storage/azdatalake/assets.json | 2 +- sdk/storage/azdatalake/directory/client.go | 203 +- .../azdatalake/directory/client_test.go | 2215 +++++++++++++++++ sdk/storage/azdatalake/directory/models.go | 131 +- sdk/storage/azdatalake/directory/responses.go | 5 +- sdk/storage/azdatalake/file/client.go | 6 +- sdk/storage/azdatalake/file/models.go | 110 +- sdk/storage/azdatalake/filesystem/client.go | 6 +- .../azdatalake/internal/exported/exported.go | 6 +- .../azdatalake/internal/generated/autorest.md | 19 +- .../internal/generated/zz_path_client.go | 12 +- .../internal/generated/zz_service_client.go | 4 +- .../azdatalake/internal/path/models.go | 56 +- .../internal/testcommon/clients_auth.go | 23 + .../azdatalake/internal/testcommon/common.go | 5 + 15 files changed, 2582 insertions(+), 221 deletions(-) create mode 100644 sdk/storage/azdatalake/directory/client_test.go diff --git a/sdk/storage/azdatalake/assets.json b/sdk/storage/azdatalake/assets.json index 0f87599cacfb..67a2df2961e2 100644 --- a/sdk/storage/azdatalake/assets.json +++ b/sdk/storage/azdatalake/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/storage/azdatalake", - "Tag": "go/storage/azdatalake_db1de4a48b" + "Tag": "go/storage/azdatalake_9dd1cc3e0e" } \ No newline at end of file diff --git a/sdk/storage/azdatalake/directory/client.go b/sdk/storage/azdatalake/directory/client.go index b1e0fc9dec56..8c998a0f812b 100644 --- a/sdk/storage/azdatalake/directory/client.go +++ b/sdk/storage/azdatalake/directory/client.go @@ -12,10 +12,17 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/datalakeerror" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/base" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/generated" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/path" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/shared" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/sas" + "net/http" + "net/url" + "strings" + "time" ) // ClientOptions contains the optional parameters when creating a Client. @@ -38,7 +45,7 @@ func NewClient(directoryURL string, cred azcore.TokenCredential, options *Client } base.SetPipelineOptions((*base.ClientOptions)(conOptions), &plOpts) - azClient, err := azcore.NewClient(shared.FileClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) + azClient, err := azcore.NewClient(shared.DirectoryClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) if err != nil { return nil, err } @@ -154,6 +161,10 @@ func (d *Client) blobClient() *blockblob.Client { return blobClient } +func (d *Client) getClientOptions() *base.ClientOptions { + return base.GetCompositeClientOptions((*base.CompositeClient[generated.PathClient, generated.PathClient, blockblob.Client])(d)) +} + func (d *Client) sharedKey() *exported.SharedKeyCredential { return base.SharedKeyComposite((*base.CompositeClient[generated.PathClient, generated.PathClient, blockblob.Client])(d)) } @@ -176,63 +187,187 @@ func (d *Client) BlobURL() string { // Create creates a new directory (dfs1). func (d *Client) Create(ctx context.Context, options *CreateOptions) (CreateResponse, error) { - return CreateResponse{}, nil + lac, mac, httpHeaders, createOpts, cpkOpts := options.format() + resp, err := d.generatedDirClientWithDFS().Create(ctx, createOpts, httpHeaders, lac, mac, nil, cpkOpts) + err = exported.ConvertToDFSError(err) + return resp, err } -// Delete removes the directory (dfs1). +// Delete deletes directory and any path under it (dfs1). func (d *Client) Delete(ctx context.Context, options *DeleteOptions) (DeleteResponse, error) { - //TODO: pass recursive = true - return DeleteResponse{}, nil + lac, mac, deleteOpts := path.FormatDeleteOptions(options, true) + resp, err := d.generatedDirClientWithDFS().Delete(ctx, deleteOpts, lac, mac) + err = exported.ConvertToDFSError(err) + return resp, err } -// GetProperties returns the properties of the directory (blob3). #TODO: we may just move this method to path client +// GetProperties gets the properties of a directory (blob3) func (d *Client) GetProperties(ctx context.Context, options *GetPropertiesOptions) (GetPropertiesResponse, error) { - // TODO: format blob response to path response - return GetPropertiesResponse{}, nil + opts := path.FormatGetPropertiesOptions(options) + var respFromCtx *http.Response + ctxWithResp := runtime.WithCaptureResponse(ctx, &respFromCtx) + resp, err := d.blobClient().GetProperties(ctxWithResp, opts) + newResp := path.FormatGetPropertiesResponse(&resp, respFromCtx) + err = exported.ConvertToDFSError(err) + return newResp, err } -// Rename renames the directory (dfs1). +func (d *Client) renamePathInURL(newName string) (string, string, string) { + endpoint := d.DFSURL() + separator := "/" + // Find the index of the last occurrence of the separator + lastIndex := strings.LastIndex(endpoint, separator) + // Split the string based on the last occurrence of the separator + firstPart := endpoint[:lastIndex] // From the beginning of the string to the last occurrence of the separator + newPathURL, newBlobURL := shared.GetURLs(runtime.JoinPaths(firstPart, newName)) + parsedNewURL, _ := url.Parse(d.DFSURL()) + return parsedNewURL.Path, newPathURL, newBlobURL +} + +// Rename renames a directory (dfs1) func (d *Client) Rename(ctx context.Context, newName string, options *RenameOptions) (RenameResponse, error) { - return RenameResponse{}, nil + newPathWithoutURL, newBlobURL, newPathURL := d.renamePathInURL(newName) + lac, mac, smac, createOpts := path.FormatRenameOptions(options, newPathWithoutURL) + var newBlobClient *blockblob.Client + var err error + if d.identityCredential() != nil { + newBlobClient, err = blockblob.NewClient(newBlobURL, *d.identityCredential(), nil) + } else if d.sharedKey() != nil { + blobSharedKey, _ := d.sharedKey().ConvertToBlobSharedKey() + newBlobClient, err = blockblob.NewClientWithSharedKeyCredential(newBlobURL, blobSharedKey, nil) + } else { + newBlobClient, err = blockblob.NewClientWithNoCredential(newBlobURL, nil) + } + if err != nil { + return RenameResponse{}, err + } + newDirClient := (*Client)(base.NewPathClient(newPathURL, newBlobURL, newBlobClient, d.generatedDirClientWithDFS().InternalClient().WithClientName(shared.DirectoryClient), d.sharedKey(), d.identityCredential(), d.getClientOptions())) + resp, err := newDirClient.generatedDirClientWithDFS().Create(ctx, createOpts, nil, lac, mac, smac, nil) + return RenameResponse{ + Response: resp, + NewDirectoryClient: newDirClient, + }, exported.ConvertToDFSError(err) } -// SetAccessControl sets the owner, owning group, and permissions for a file or directory (dfs1). +// SetAccessControl sets the owner, owning group, and permissions for a directory (dfs1). func (d *Client) SetAccessControl(ctx context.Context, options *SetAccessControlOptions) (SetAccessControlResponse, error) { - return SetAccessControlResponse{}, nil + opts, lac, mac, err := path.FormatSetAccessControlOptions(options) + if err != nil { + return SetAccessControlResponse{}, err + } + resp, err := d.generatedDirClientWithDFS().SetAccessControl(ctx, opts, lac, mac) + err = exported.ConvertToDFSError(err) + return resp, err } -// SetAccessControlRecursive sets the owner, owning group, and permissions for a file or directory (dfs1). -func (d *Client) SetAccessControlRecursive(ctx context.Context, options *SetAccessControlRecursiveOptions) (SetAccessControlRecursiveResponse, error) { - // TODO explicitly pass SetAccessControlRecursiveMode - return SetAccessControlRecursiveResponse{}, nil +func (d *Client) setAccessControlHelper(mode generated.PathSetAccessControlRecursiveMode, listOptions *generated.PathClientSetAccessControlRecursiveOptions) *runtime.Pager[SetAccessControlRecursiveResponse] { + return runtime.NewPager(runtime.PagingHandler[SetAccessControlRecursiveResponse]{ + More: func(page SetAccessControlRecursiveResponse) bool { + return page.Continuation != nil && len(*page.Continuation) > 0 + }, + Fetcher: func(ctx context.Context, page *SetAccessControlRecursiveResponse) (SetAccessControlRecursiveResponse, error) { + var req *policy.Request + var err error + if page == nil { + req, err = d.generatedDirClientWithDFS().SetAccessControlRecursiveCreateRequest(ctx, mode, listOptions) + err = exported.ConvertToDFSError(err) + } else { + listOptions.Continuation = page.Continuation + req, err = d.generatedDirClientWithDFS().SetAccessControlRecursiveCreateRequest(ctx, mode, listOptions) + err = exported.ConvertToDFSError(err) + } + if err != nil { + return SetAccessControlRecursiveResponse{}, err + } + resp, err := d.generatedDirClientWithDFS().InternalClient().Pipeline().Do(req) + err = exported.ConvertToDFSError(err) + if err != nil { + return SetAccessControlRecursiveResponse{}, err + } + if !runtime.HasStatusCode(resp, http.StatusOK) { + return SetAccessControlRecursiveResponse{}, runtime.NewResponseError(resp) + } + newResp, err := d.generatedDirClientWithDFS().SetAccessControlRecursiveHandleResponse(resp) + return newResp, exported.ConvertToDFSError(err) + }, + }) + } -// UpdateAccessControlRecursive updates the owner, owning group, and permissions for a file or directory (dfs1). -func (d *Client) UpdateAccessControlRecursive(ctx context.Context, options *UpdateAccessControlRecursiveOptions) (UpdateAccessControlRecursiveResponse, error) { - // TODO explicitly pass SetAccessControlRecursiveMode - return SetAccessControlRecursiveResponse{}, nil +// NewSetAccessControlRecursivePager sets the owner, owning group, and permissions for a directory (dfs1). +func (d *Client) NewSetAccessControlRecursivePager(ACL string, options *SetAccessControlRecursiveOptions) *runtime.Pager[SetAccessControlRecursiveResponse] { + mode, listOptions := options.format(ACL, "set") + return d.setAccessControlHelper(mode, listOptions) } -// GetAccessControl gets the owner, owning group, and permissions for a file or directory (dfs1). -func (d *Client) GetAccessControl(ctx context.Context, options *GetAccessControlOptions) (GetAccessControlResponse, error) { - return GetAccessControlResponse{}, nil +// NewUpdateAccessControlRecursivePager updates the owner, owning group, and permissions for a directory (dfs1). +func (d *Client) NewUpdateAccessControlRecursivePager(ACL string, options *UpdateAccessControlRecursiveOptions) *runtime.Pager[UpdateAccessControlRecursiveResponse] { + mode, listOptions := options.format(ACL, "modify") + return d.setAccessControlHelper(mode, listOptions) +} + +// NewRemoveAccessControlRecursivePager removes the owner, owning group, and permissions for a directory (dfs1). +func (d *Client) NewRemoveAccessControlRecursivePager(ACL string, options *RemoveAccessControlRecursiveOptions) *runtime.Pager[RemoveAccessControlRecursiveResponse] { + mode, listOptions := options.format(ACL, "remove") + return d.setAccessControlHelper(mode, listOptions) } -// RemoveAccessControlRecursive removes the owner, owning group, and permissions for a file or directory (dfs1). -func (d *Client) RemoveAccessControlRecursive(ctx context.Context, options *RemoveAccessControlRecursiveOptions) (RemoveAccessControlRecursiveResponse, error) { - // TODO explicitly pass SetAccessControlRecursiveMode - return SetAccessControlRecursiveResponse{}, nil +// GetAccessControl gets the owner, owning group, and permissions for a directory (dfs1). +func (d *Client) GetAccessControl(ctx context.Context, options *GetAccessControlOptions) (GetAccessControlResponse, error) { + opts, lac, mac := path.FormatGetAccessControlOptions(options) + resp, err := d.generatedDirClientWithDFS().GetProperties(ctx, opts, lac, mac) + err = exported.ConvertToDFSError(err) + return resp, err } -// SetMetadata sets the metadata for a file or directory (blob3). +// SetMetadata sets the metadata for a directory (blob3). func (d *Client) SetMetadata(ctx context.Context, options *SetMetadataOptions) (SetMetadataResponse, error) { - // TODO: call directly into blob - return SetMetadataResponse{}, nil + opts, metadata := path.FormatSetMetadataOptions(options) + resp, err := d.blobClient().SetMetadata(ctx, metadata, opts) + err = exported.ConvertToDFSError(err) + return resp, err } -// SetHTTPHeaders sets the HTTP headers for a file or directory (blob3). +// SetHTTPHeaders sets the HTTP headers for a directory (blob3). func (d *Client) SetHTTPHeaders(ctx context.Context, httpHeaders HTTPHeaders, options *SetHTTPHeadersOptions) (SetHTTPHeadersResponse, error) { - // TODO: call formatBlobHTTPHeaders() since we want to add the blob prefix to our options before calling into blob - // TODO: call into blob - return SetHTTPHeadersResponse{}, nil + opts, blobHTTPHeaders := path.FormatSetHTTPHeadersOptions(options, httpHeaders) + resp, err := d.blobClient().SetHTTPHeaders(ctx, blobHTTPHeaders, opts) + newResp := SetHTTPHeadersResponse{} + path.FormatSetHTTPHeadersResponse(&newResp, &resp) + err = exported.ConvertToDFSError(err) + return newResp, err +} + +// GetSASURL is a convenience method for generating a SAS token for the currently pointed at blob. +// It can only be used if the credential supplied during creation was a SharedKeyCredential. +func (f *Client) GetSASURL(permissions sas.DirectoryPermissions, expiry time.Time, o *GetSASURLOptions) (string, error) { + if f.sharedKey() == nil { + return "", datalakeerror.MissingSharedKeyCredential + } + + urlParts, err := sas.ParseURL(f.BlobURL()) + err = exported.ConvertToDFSError(err) + if err != nil { + return "", err + } + + st := path.FormatGetSASURLOptions(o) + + qps, err := sas.DatalakeSignatureValues{ + DirectoryPath: urlParts.PathName, + FilesystemName: urlParts.FilesystemName, + Version: sas.Version, + Permissions: permissions.String(), + StartTime: st, + ExpiryTime: expiry.UTC(), + }.SignWithSharedKey(f.sharedKey()) + + err = exported.ConvertToDFSError(err) + if err != nil { + return "", err + } + + endpoint := f.BlobURL() + "?" + qps.Encode() + + return endpoint, nil } diff --git a/sdk/storage/azdatalake/directory/client_test.go b/sdk/storage/azdatalake/directory/client_test.go new file mode 100644 index 000000000000..2a11a11d0751 --- /dev/null +++ b/sdk/storage/azdatalake/directory/client_test.go @@ -0,0 +1,2215 @@ +//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 directory_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/azdatalake/datalakeerror" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/directory" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/testcommon" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/sas" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "testing" + "time" +) + +var proposedLeaseIDs = []*string{to.Ptr("c820a799-76d7-4ee2-6e15-546f19325c2c"), to.Ptr("326cc5e1-746e-4af8-4811-a50e6629a8ca")} + +func Test(t *testing.T) { + recordMode := recording.GetRecordMode() + t.Logf("Running datalake Tests in %s mode\n", recordMode) + if recordMode == recording.LiveMode { + suite.Run(t, &RecordedTestSuite{}) + suite.Run(t, &UnrecordedTestSuite{}) + } else if recordMode == recording.PlaybackMode { + suite.Run(t, &RecordedTestSuite{}) + } else if recordMode == recording.RecordingMode { + suite.Run(t, &RecordedTestSuite{}) + } +} + +func (s *RecordedTestSuite) BeforeTest(suite string, test string) { + testcommon.BeforeTest(s.T(), suite, test) +} + +func (s *RecordedTestSuite) AfterTest(suite string, test string) { + testcommon.AfterTest(s.T(), suite, test) +} + +func (s *UnrecordedTestSuite) BeforeTest(suite string, test string) { + +} + +func (s *UnrecordedTestSuite) AfterTest(suite string, test string) { + +} + +type RecordedTestSuite struct { + suite.Suite +} + +type UnrecordedTestSuite struct { + suite.Suite +} + +func (s *RecordedTestSuite) TestCreateDirAndDelete() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateDirWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + createDirOpts := &directory.CreateOptions{ + AccessConditions: nil, + } + + resp, err = dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateDirIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + DirName := testcommon.GenerateDirName(testName) + DirClient, err := testcommon.GetDirClient(filesystemName, DirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, DirClient) + + resp, err := DirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + resp, err = DirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateDirIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + DirName := testcommon.GenerateDirName(testName) + DirClient, err := testcommon.GetDirClient(filesystemName, DirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, DirClient) + + resp, err := DirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + resp, err = DirClient.Create(context.Background(), createDirOpts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestCreateDirIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + resp, err = dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateFileIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + resp, err = dirClient.Create(context.Background(), createDirOpts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestCreateFileIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfMatch: etag, + }, + }, + } + + resp, err = dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateFileIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + createDirOpts := &directory.CreateOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfNoneMatch: etag, + }, + }, + } + + resp, err = dirClient.Create(context.Background(), createDirOpts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestCreateFileWithMetadataNotNil() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createDirOpts := &directory.CreateOptions{ + Metadata: testcommon.BasicMetadata, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateFileWithEmptyMetadata() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createDirOpts := &directory.CreateOptions{ + Metadata: nil, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateFileWithNilHTTPHeaders() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createDirOpts := &directory.CreateOptions{ + HTTPHeaders: nil, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateFileWithHTTPHeaders() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createDirOpts := &directory.CreateOptions{ + HTTPHeaders: &testcommon.BasicHeaders, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), createDirOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateDirWithLease() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createFileOpts := &directory.CreateOptions{ + ProposedLeaseID: proposedLeaseIDs[0], + LeaseDuration: to.Ptr(int64(15)), + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createFileOpts) + _require.Nil(err) + _require.NotNil(resp) + + // should fail since leased + _, err = dirClient.Create(context.Background(), createFileOpts) + _require.NotNil(err) + + time.Sleep(time.Second * 15) + resp, err = dirClient.Create(context.Background(), createFileOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestCreateDirWithPermissions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + perms := "0777" + umask := "0000" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createFileOpts := &directory.CreateOptions{ + Permissions: &perms, + Umask: &umask, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createFileOpts) + _require.Nil(err) + _require.NotNil(resp) + + resp2, err := dirClient.GetProperties(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp2) + _require.Equal("rwxrwxrwx", *resp2.Permissions) +} + +func (s *RecordedTestSuite) TestCreateDirWithOwnerGroupACLUmask() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + umask := "0000" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createFileOpts := &directory.CreateOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + Umask: &umask, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createFileOpts) + _require.Nil(err) + _require.NotNil(resp) + +} + +func (s *RecordedTestSuite) TestDeleteDirWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + _, err = dirClient.Create(context.Background(), nil) + _require.Nil(err) + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: nil, + } + + resp, err := dirClient.Delete(context.Background(), deleteOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestDeleteDirIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + resp1, err := dirClient.Delete(context.Background(), deleteOpts) + _require.Nil(err) + _require.NotNil(resp1) +} + +func (s *RecordedTestSuite) TestDeleteDirIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.Delete(context.Background(), deleteOpts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestDeleteDirIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.Delete(context.Background(), deleteOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestDeleteDirIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.Delete(context.Background(), deleteOpts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestDeleteDirIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfMatch: etag, + }, + }, + } + + _, err = dirClient.Delete(context.Background(), deleteOpts) + _require.Nil(err) + _require.NotNil(resp) +} + +func (s *RecordedTestSuite) TestDeleteDirIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + deleteOpts := &directory.DeleteOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfNoneMatch: etag, + }, + }, + } + + _, err = dirClient.Delete(context.Background(), deleteOpts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestSetAccessControlNil() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + _, err = dirClient.SetAccessControl(context.Background(), nil) + _require.NotNil(err) + + _require.Equal(err, datalakeerror.MissingParameters) +} + +// TODO: write test that fails if you provide permissions and acls +func (s *RecordedTestSuite) TestSetAccessControl() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetAccessControlWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: nil, + } + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }} + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + etag := resp.ETag + + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfMatch: etag, + }, + }, + } + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetAccessControlIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + owner := "4cf4e284-f6a8-4540-b53e-c3469af032dc" + group := owner + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + opts := &directory.SetAccessControlOptions{ + Owner: &owner, + Group: &group, + ACL: &acl, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfNoneMatch: etag, + }, + }} + + _, err = dirClient.SetAccessControl(context.Background(), opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestGetAccessControl() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + getACLResp, err := dirClient.GetAccessControl(context.Background(), nil) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestGetAccessControlWithSAS() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + // Adding SAS and options + permissions := sas.DirectoryPermissions{ + Read: true, + Add: true, + Write: true, + Create: true, + Delete: true, + } + expiry := time.Now().Add(time.Hour) + + sasURL, err := dirClient.GetSASURL(permissions, expiry, nil) + _require.Nil(err) + + dirClient2, _ := directory.NewClientWithNoCredential(sasURL, nil) + + getACLResp, err := dirClient2.GetAccessControl(context.Background(), nil) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestDeleteWithSAS() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + // Adding SAS and options + permissions := sas.DirectoryPermissions{ + Read: true, + Add: true, + Write: true, + Create: true, + Delete: true, + } + expiry := time.Now().Add(time.Hour) + + sasURL, err := dirClient.GetSASURL(permissions, expiry, nil) + _require.Nil(err) + + dirClient2, _ := directory.NewClientWithNoCredential(sasURL, nil) + + _, err = dirClient2.Delete(context.Background(), nil) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestGetAccessControlWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + opts := &directory.GetAccessControlOptions{ + AccessConditions: nil, + } + + getACLResp, err := dirClient.GetAccessControl(context.Background(), opts) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + getACLResp, err := dirClient.GetAccessControl(context.Background(), opts) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.GetAccessControl(context.Background(), opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }} + + getACLResp, err := dirClient.GetAccessControl(context.Background(), opts) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.GetAccessControl(context.Background(), opts) + _require.NotNil(err) + + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + etag := resp.ETag + + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfMatch: etag, + }, + }, + } + + getACLResp, err := dirClient.GetAccessControl(context.Background(), opts) + _require.Nil(err) + _require.Equal(acl, *getACLResp.ACL) +} + +func (s *RecordedTestSuite) TestGetAccessControlIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + opts := &directory.GetAccessControlOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfNoneMatch: etag, + }, + }} + + _, err = dirClient.GetAccessControl(context.Background(), opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +///===================================================================== + +func (s *RecordedTestSuite) TestUpdateAccessControlRecursive() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "user::rwx,group::r-x,other::rwx" + acl1 := "user::rwx,group::r--,other::r--" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + createOpts := &directory.CreateOptions{ + ACL: &acl, + } + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), createOpts) + _require.Nil(err) + _require.NotNil(resp) + + pager := dirClient.NewUpdateAccessControlRecursivePager(acl1, nil) + + for pager.More() { + resp1, err := pager.NextPage(context.Background()) + _require.Nil(err) + _require.Equal(*resp1.SetAccessControlRecursiveResponse.DirectoriesSuccessful, int32(1)) + if err != nil { + break + } + } +} + +func (s *RecordedTestSuite) TestRemoveAccessControlRecursive() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + acl := "mask," + "default:user,default:group," + + "user:ec3595d6-2c17-4696-8caa-7e139758d24a,group:ec3595d6-2c17-4696-8caa-7e139758d24a," + + "default:user:ec3595d6-2c17-4696-8caa-7e139758d24a,default:group:ec3595d6-2c17-4696-8caa-7e139758d24a" + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + pager := dirClient.NewRemoveAccessControlRecursivePager(acl, nil) + + for pager.More() { + resp1, err := pager.NextPage(context.Background()) + _require.Nil(err) + _require.Equal(*resp1.SetAccessControlRecursiveResponse.DirectoriesSuccessful, int32(1)) + if err != nil { + break + } + } +} + +func (s *RecordedTestSuite) TestSetMetadataNil() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + _, err = dirClient.SetMetadata(context.Background(), nil) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetMetadataWithEmptyOpts() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + opts := &directory.SetMetadataOptions{ + Metadata: nil, + } + _, err = dirClient.SetMetadata(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetMetadataWithBasicMetadata() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + opts := &directory.SetMetadataOptions{ + Metadata: testcommon.BasicMetadata, + } + _, err = dirClient.SetMetadata(context.Background(), opts) + _require.Nil(err) +} + +func (s *RecordedTestSuite) TestSetMetadataWithAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + defer testcommon.DeleteDir(context.Background(), _require, dirClient) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + opts := &directory.SetMetadataOptions{ + Metadata: testcommon.BasicMetadata, + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + _, err = dirClient.SetMetadata(context.Background(), opts) + _require.Nil(err) +} + +func validatePropertiesSet(_require *require.Assertions, dirClient *directory.Client, disposition string) { + resp, err := dirClient.GetProperties(context.Background(), nil) + _require.Nil(err) + _require.Equal(*resp.ContentDisposition, disposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeaders() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, nil) + _require.Nil(err) + validatePropertiesSet(_require, dirClient, *testcommon.BasicHeaders.ContentDisposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: nil, + } + + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.Nil(err) + validatePropertiesSet(_require, dirClient, *testcommon.BasicHeaders.ContentDisposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.Nil(err) + validatePropertiesSet(_require, dirClient, *testcommon.BasicHeaders.ContentDisposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfModifiedSince: ¤tTime, + }, + }, + } + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.Nil(err) + validatePropertiesSet(_require, dirClient, *testcommon.BasicHeaders.ContentDisposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfUnmodifiedSince: ¤tTime, + }, + }, + } + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfMatch: etag, + }, + }} + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.Nil(err) + validatePropertiesSet(_require, dirClient, *testcommon.BasicHeaders.ContentDisposition) +} + +func (s *RecordedTestSuite) TestSetHTTPHeadersIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + opts := &directory.SetHTTPHeadersOptions{ + AccessConditions: &directory.AccessConditions{ + ModifiedAccessConditions: &directory.ModifiedAccessConditions{ + IfNoneMatch: etag, + }, + }, + } + _, err = dirClient.SetHTTPHeaders(context.Background(), testcommon.BasicHeaders, opts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.ConditionNotMet) +} + +func (s *RecordedTestSuite) TestRenameNoOptions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + resp1, err := dirClient.Rename(context.Background(), "newName", nil) + _require.Nil(err) + _require.NotNil(resp1) + _require.Contains(resp1.NewDirectoryClient.DFSURL(), "newName") +} + +func (s *RecordedTestSuite) TestRenameFileWithNilAccessConditions() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + renameFileOpts := &directory.RenameOptions{ + AccessConditions: nil, + } + + resp1, err := dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.Nil(err) + _require.NotNil(resp1) + _require.Contains(resp1.NewDirectoryClient.DFSURL(), "newName") +} + +func (s *RecordedTestSuite) TestRenameFileIfModifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfModifiedSince: ¤tTime, + }, + }, + } + resp1, err := dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.Nil(err) + _require.NotNil(resp1) + _require.Contains(resp1.NewDirectoryClient.DFSURL(), "newName") +} + +func (s *RecordedTestSuite) TestRenameFileIfModifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfModifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.SourceConditionNotMet) +} + +func (s *RecordedTestSuite) TestRenameFileIfUnmodifiedSinceTrue() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, 10) + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfUnmodifiedSince: ¤tTime, + }, + }, + } + + resp1, err := dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.NotNil(resp1) + _require.Contains(resp1.NewDirectoryClient.DFSURL(), "newName") +} + +func (s *RecordedTestSuite) TestRenameFileIfUnmodifiedSinceFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + currentTime := testcommon.GetRelativeTimeFromAnchor(resp.Date, -10) + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfUnmodifiedSince: ¤tTime, + }, + }, + } + + _, err = dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.SourceConditionNotMet) +} + +func (s *RecordedTestSuite) TestRenameFileIfETagMatch() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfMatch: etag, + }, + }, + } + + resp1, err := dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.NotNil(resp1) + _require.Contains(resp1.NewDirectoryClient.DFSURL(), "newName") +} + +func (s *RecordedTestSuite) TestRenameFileIfETagMatchFalse() { + _require := require.New(s.T()) + testName := s.T().Name() + + filesystemName := testcommon.GenerateFilesystemName(testName) + fsClient, err := testcommon.GetFilesystemClient(filesystemName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + defer testcommon.DeleteFilesystem(context.Background(), _require, fsClient) + + _, err = fsClient.Create(context.Background(), nil) + _require.Nil(err) + + dirName := testcommon.GenerateDirName(testName) + dirClient, err := testcommon.GetDirClient(filesystemName, dirName, s.T(), testcommon.TestAccountDatalake, nil) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.Nil(err) + _require.NotNil(resp) + + etag := resp.ETag + + renameFileOpts := &directory.RenameOptions{ + SourceAccessConditions: &directory.SourceAccessConditions{ + SourceModifiedAccessConditions: &directory.SourceModifiedAccessConditions{ + SourceIfNoneMatch: etag, + }, + }, + } + + _, err = dirClient.Rename(context.Background(), "newName", renameFileOpts) + _require.NotNil(err) + testcommon.ValidateErrorCode(_require, err, datalakeerror.SourceConditionNotMet) +} + +// TODO: more tests for acls diff --git a/sdk/storage/azdatalake/directory/models.go b/sdk/storage/azdatalake/directory/models.go index ff71be61b95d..517fdf0cda44 100644 --- a/sdk/storage/azdatalake/directory/models.go +++ b/sdk/storage/azdatalake/directory/models.go @@ -10,10 +10,9 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/generated" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/internal/path" - "time" ) -// CreateOptions contains the optional parameters when calling the Create operation. dfs endpoint +// CreateOptions contains the optional parameters when calling the Create operation. dfs endpoint. type CreateOptions struct { // AccessConditions contains parameters for accessing the file. AccessConditions *AccessConditions @@ -23,9 +22,10 @@ type CreateOptions struct { CPKInfo *CPKInfo // HTTPHeaders contains the HTTP headers for path operations. HTTPHeaders *HTTPHeaders - //PathExpiryOptions *ExpiryOptions - // LeaseDuration specifies the duration of the lease. - LeaseDuration *time.Duration + // LeaseDuration 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. + LeaseDuration *int64 // ProposedLeaseID specifies the proposed lease ID for the file. ProposedLeaseID *string // Permissions is the octal representation of the permissions for user, group and mask. @@ -40,97 +40,78 @@ type CreateOptions struct { ACL *string } -func (o *CreateOptions) format() (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.PathHTTPHeaders, error) { - // TODO: add all other required options for the create operation, we don't need sourceModAccCond since this is not rename +func (o *CreateOptions) format() (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.PathHTTPHeaders, *generated.PathClientCreateOptions, *generated.CPKInfo) { + resource := generated.PathResourceTypeDirectory + createOpts := &generated.PathClientCreateOptions{ + Resource: &resource, + } + if o == nil { + return nil, nil, nil, createOpts, nil + } leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) - httpHeaders := &generated.PathHTTPHeaders{ - CacheControl: o.HTTPHeaders.CacheControl, - ContentDisposition: o.HTTPHeaders.ContentDisposition, - ContentEncoding: o.HTTPHeaders.ContentEncoding, - ContentLanguage: o.HTTPHeaders.ContentLanguage, - ContentMD5: o.HTTPHeaders.ContentMD5, - ContentType: o.HTTPHeaders.ContentType, - TransactionalContentHash: o.HTTPHeaders.ContentMD5, + createOpts.ACL = o.ACL + createOpts.Group = o.Group + createOpts.Owner = o.Owner + createOpts.Umask = o.Umask + createOpts.Permissions = o.Permissions + createOpts.ProposedLeaseID = o.ProposedLeaseID + createOpts.LeaseDuration = o.LeaseDuration + + var httpHeaders *generated.PathHTTPHeaders + var cpkOpts *generated.CPKInfo + + if o.HTTPHeaders != nil { + httpHeaders = path.FormatPathHTTPHeaders(o.HTTPHeaders) } - return leaseAccessConditions, modifiedAccessConditions, httpHeaders, nil + if o.CPKInfo != nil { + cpkOpts = &generated.CPKInfo{ + EncryptionAlgorithm: o.CPKInfo.EncryptionAlgorithm, + EncryptionKey: o.CPKInfo.EncryptionKey, + EncryptionKeySHA256: o.CPKInfo.EncryptionKeySHA256, + } + } + return leaseAccessConditions, modifiedAccessConditions, httpHeaders, createOpts, cpkOpts } // DeleteOptions contains the optional parameters when calling the Delete operation. dfs endpoint -type DeleteOptions struct { - // AccessConditions specifies parameters for accessing the directory - AccessConditions *AccessConditions -} +type DeleteOptions = path.DeleteOptions -func (o *DeleteOptions) format() (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, error) { - leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) - return leaseAccessConditions, modifiedAccessConditions, nil -} - -type RenameOptions struct { - // SourceModifiedAccessConditions specifies parameters for accessing the source directory - SourceModifiedAccessConditions *SourceModifiedAccessConditions - // AccessConditions specifies parameters for accessing the destination directory - AccessConditions *AccessConditions -} +// RenameOptions contains the optional parameters when calling the Rename operation. +type RenameOptions = path.RenameOptions // ===================================== PATH IMPORTS =========================================== // SetAccessControlRecursiveOptions contains the optional parameters when calling the SetAccessControlRecursive operation. TODO: Design formatter -type SetAccessControlRecursiveOptions struct { - // ACL is the access control list for the path. - ACL *string - // BatchSize is the number of paths to set access control recursively in a single call. - BatchSize *int32 - // MaxBatches is the maximum number of batches to perform the operation on. - MaxBatches *int32 +type accessControlRecursiveOptions struct { + MaxResults *int32 // ContinueOnFailure indicates whether to continue on failure when the operation encounters an error. ContinueOnFailure *bool // Marker is the continuation token to use when continuing the operation. Marker *string } -func (o *SetAccessControlRecursiveOptions) format() (*generated.PathClientSetAccessControlRecursiveOptions, error) { - // TODO: design formatter - return nil, nil +func (o *accessControlRecursiveOptions) format(ACL, mode string) (generated.PathSetAccessControlRecursiveMode, *generated.PathClientSetAccessControlRecursiveOptions) { + opts := &generated.PathClientSetAccessControlRecursiveOptions{ + ACL: &ACL, + } + newMode := generated.PathSetAccessControlRecursiveMode(mode) + if o == nil { + return newMode, opts + } + opts.ForceFlag = o.ContinueOnFailure + opts.Continuation = o.Marker + opts.MaxRecords = o.MaxResults + return newMode, opts } -// UpdateAccessControlRecursiveOptions contains the optional parameters when calling the UpdateAccessControlRecursive operation. TODO: Design formatter -type UpdateAccessControlRecursiveOptions struct { - // ACL is the access control list for the path. - ACL *string - // BatchSize is the number of paths to set access control recursively in a single call. - BatchSize *int32 - // MaxBatches is the maximum number of batches to perform the operation on. - MaxBatches *int32 - // ContinueOnFailure indicates whether to continue on failure when the operation encounters an error. - ContinueOnFailure *bool - // Marker is the continuation token to use when continuing the operation. - Marker *string -} +// SetAccessControlRecursiveOptions contains the optional parameters when calling the UpdateAccessControlRecursive operation. TODO: Design formatter +type SetAccessControlRecursiveOptions = accessControlRecursiveOptions -func (o *UpdateAccessControlRecursiveOptions) format() (*generated.PathClientSetAccessControlRecursiveOptions, error) { - // TODO: design formatter - similar to SetAccessControlRecursiveOptions - return nil, nil -} +// UpdateAccessControlRecursiveOptions contains the optional parameters when calling the UpdateAccessControlRecursive operation. TODO: Design formatter +type UpdateAccessControlRecursiveOptions = accessControlRecursiveOptions // RemoveAccessControlRecursiveOptions contains the optional parameters when calling the RemoveAccessControlRecursive operation. TODO: Design formatter -type RemoveAccessControlRecursiveOptions struct { - // ACL is the access control list for the path. - ACL *string - // BatchSize is the number of paths to set access control recursively in a single call. - BatchSize *int32 - // MaxBatches is the maximum number of batches to perform the operation on. - MaxBatches *int32 - // ContinueOnFailure indicates whether to continue on failure when the operation encounters an error. - ContinueOnFailure *bool - // Marker is the continuation token to use when continuing the operation. - Marker *string -} - -func (o *RemoveAccessControlRecursiveOptions) format() (*generated.PathClientSetAccessControlRecursiveOptions, error) { - // TODO: design formatter - similar to SetAccessControlRecursiveOptions - return nil, nil -} +type RemoveAccessControlRecursiveOptions = accessControlRecursiveOptions // ================================= path imports ================================== diff --git a/sdk/storage/azdatalake/directory/responses.go b/sdk/storage/azdatalake/directory/responses.go index e87136e9b0a6..dc2ad337ddb9 100644 --- a/sdk/storage/azdatalake/directory/responses.go +++ b/sdk/storage/azdatalake/directory/responses.go @@ -18,7 +18,10 @@ type CreateResponse = generated.PathClientCreateResponse type DeleteResponse = generated.PathClientDeleteResponse // RenameResponse contains the response fields for the Create operation. -type RenameResponse = generated.PathClientCreateResponse +type RenameResponse struct { + Response generated.PathClientCreateResponse + NewDirectoryClient *Client +} // SetAccessControlRecursiveResponse contains the response fields for the SetAccessControlRecursive operation. type SetAccessControlRecursiveResponse = generated.PathClientSetAccessControlRecursiveResponse diff --git a/sdk/storage/azdatalake/file/client.go b/sdk/storage/azdatalake/file/client.go index 0fae799fb965..7400838cf7fb 100644 --- a/sdk/storage/azdatalake/file/client.go +++ b/sdk/storage/azdatalake/file/client.go @@ -200,7 +200,7 @@ func (f *Client) Create(ctx context.Context, options *CreateOptions) (CreateResp // Delete deletes a file (dfs1). func (f *Client) Delete(ctx context.Context, options *DeleteOptions) (DeleteResponse, error) { - lac, mac, deleteOpts := options.format() + lac, mac, deleteOpts := path.FormatDeleteOptions(options, false) resp, err := f.generatedFileClientWithDFS().Delete(ctx, deleteOpts, lac, mac) err = exported.ConvertToDFSError(err) return resp, err @@ -232,7 +232,7 @@ func (f *Client) renamePathInURL(newName string) (string, string, string) { // Rename renames a file (dfs1) func (f *Client) Rename(ctx context.Context, newName string, options *RenameOptions) (RenameResponse, error) { newPathWithoutURL, newBlobURL, newPathURL := f.renamePathInURL(newName) - lac, mac, smac, createOpts := options.format(newPathWithoutURL) + lac, mac, smac, createOpts := path.FormatRenameOptions(options, newPathWithoutURL) var newBlobClient *blockblob.Client var err error if f.identityCredential() != nil { @@ -377,7 +377,7 @@ func (f *Client) uploadFromReader(ctx context.Context, reader io.ReaderAt, actua return errors.New("buffer is too large to upload to a file") } if o.ChunkSize == 0 { - o.ChunkSize = MaxUpdateRangeBytes + o.ChunkSize = MaxAppendBytes } if log.Should(exported.EventUpload) { diff --git a/sdk/storage/azdatalake/file/models.go b/sdk/storage/azdatalake/file/models.go index 1f628860bded..55c2a781be83 100644 --- a/sdk/storage/azdatalake/file/models.go +++ b/sdk/storage/azdatalake/file/models.go @@ -24,14 +24,11 @@ const ( _1MiB = 1024 * 1024 CountToEnd = 0 - // MaxUpdateRangeBytes indicates the maximum number of bytes that can be updated in a call to Client.UploadRange. - MaxUpdateRangeBytes = 4 * 1024 * 1024 // 4MiB + // MaxAppendBytes indicates the maximum number of bytes that can be updated in a call to Client.UploadRange. + MaxAppendBytes = 100 * 1024 * 1024 // 100iB // MaxFileSize indicates the maximum size of the file allowed. MaxFileSize = 4 * 1024 * 1024 * 1024 * 1024 // 4 TiB - - // DefaultDownloadChunkSize is default chunk size - DefaultDownloadChunkSize = int64(4 * 1024 * 1024) // 4MiB ) // CreateOptions contains the optional parameters when calling the Create operation. dfs endpoint. @@ -97,7 +94,7 @@ func (o *CreateOptions) format() (*generated.LeaseAccessConditions, *generated.M } if o.CPKInfo != nil { cpkOpts = &generated.CPKInfo{ - EncryptionAlgorithm: (*generated.EncryptionAlgorithmType)(o.CPKInfo.EncryptionAlgorithm), + EncryptionAlgorithm: o.CPKInfo.EncryptionAlgorithm, EncryptionKey: o.CPKInfo.EncryptionKey, EncryptionKeySHA256: o.CPKInfo.EncryptionKeySHA256, } @@ -105,62 +102,6 @@ func (o *CreateOptions) format() (*generated.LeaseAccessConditions, *generated.M return leaseAccessConditions, modifiedAccessConditions, httpHeaders, createOpts, cpkOpts } -// DeleteOptions contains the optional parameters when calling the Delete operation. dfs endpoint -type DeleteOptions struct { - // AccessConditions contains parameters for accessing the file. - AccessConditions *AccessConditions -} - -func (o *DeleteOptions) format() (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.PathClientDeleteOptions) { - recursive := false - deleteOpts := &generated.PathClientDeleteOptions{ - Recursive: &recursive, - } - if o == nil { - return nil, nil, deleteOpts - } - leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) - return leaseAccessConditions, modifiedAccessConditions, deleteOpts -} - -// RenameOptions contains the optional parameters when calling the Rename operation. -type RenameOptions struct { - // SourceAccessConditions identifies the source path access conditions. - SourceAccessConditions *SourceAccessConditions - // AccessConditions contains parameters for accessing the file. - AccessConditions *AccessConditions -} - -func (o *RenameOptions) format(path string) (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.SourceModifiedAccessConditions, *generated.PathClientCreateOptions) { - // we don't need sourceModAccCond since this is not rename - mode := generated.PathRenameModeLegacy - createOpts := &generated.PathClientCreateOptions{ - Mode: &mode, - RenameSource: &path, - } - if o == nil { - return nil, nil, nil, createOpts - } - leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) - if o.SourceAccessConditions != nil { - if o.SourceAccessConditions.SourceLeaseAccessConditions != nil { - createOpts.SourceLeaseID = o.SourceAccessConditions.SourceLeaseAccessConditions.LeaseID - } - if o.SourceAccessConditions.SourceModifiedAccessConditions != nil { - sourceModifiedAccessConditions := &generated.SourceModifiedAccessConditions{ - SourceIfMatch: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfMatch, - SourceIfModifiedSince: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfModifiedSince, - SourceIfNoneMatch: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfNoneMatch, - SourceIfUnmodifiedSince: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfUnmodifiedSince, - } - return leaseAccessConditions, modifiedAccessConditions, sourceModifiedAccessConditions, createOpts - } - } - return leaseAccessConditions, modifiedAccessConditions, nil, createOpts -} - -// ===================================== PATH IMPORTS =========================================== - // UpdateAccessControlOptions contains the optional parameters when calling the UpdateAccessControlRecursive operation. type UpdateAccessControlOptions struct { //placeholder @@ -185,11 +126,11 @@ func (o *RemoveAccessControlOptions) format(ACL string) (*generated.PathClientSe }, mode } -type HTTPRange = exported.HTTPRange +// ===================================== PATH IMPORTS =========================================== // uploadFromReaderOptions identifies options used by the UploadBuffer and UploadFile functions. type uploadFromReaderOptions struct { - // ChunkSize specifies the chunk size to use in bytes; the default (and maximum size) is MaxUpdateRangeBytes. + // ChunkSize specifies the chunk size to use in bytes; the default (and maximum size) is MaxAppendBytes. ChunkSize int64 // Progress is a function that is invoked periodically as bytes are sent to the FileClient. // Note that the progress reporting is not always increasing; it can go down when retrying a request. @@ -206,7 +147,7 @@ type uploadFromReaderOptions struct { // UploadStreamOptions provides set of configurations for Client.UploadStream operation. type UploadStreamOptions struct { - // ChunkSize specifies the chunk size to use in bytes; the default (and maximum size) is MaxUpdateRangeBytes. + // ChunkSize specifies the chunk size to use in bytes; the default (and maximum size) is MaxAppendBytes. ChunkSize int64 // Concurrency indicates the maximum number of chunks to upload in parallel (default is 5) Concurrency uint16 @@ -427,10 +368,7 @@ func (o *DownloadStreamOptions) format() *blob.DownloadStreamOptions { downloadStreamOptions := &blob.DownloadStreamOptions{} if o.Range != nil { - downloadStreamOptions.Range = blob.HTTPRange{ - Offset: o.Range.Offset, - Count: o.Range.Count, - } + downloadStreamOptions.Range = *o.Range } if o.CPKInfo != nil { downloadStreamOptions.CPKInfo = &blob.CPKInfo{ @@ -442,7 +380,7 @@ func (o *DownloadStreamOptions) format() *blob.DownloadStreamOptions { downloadStreamOptions.RangeGetContentMD5 = o.RangeGetContentMD5 downloadStreamOptions.AccessConditions = exported.FormatBlobAccessConditions(o.AccessConditions) - downloadStreamOptions.CPKScopeInfo = (*blob.CPKScopeInfo)(o.CPKScopeInfo) + downloadStreamOptions.CPKScopeInfo = o.CPKScopeInfo return downloadStreamOptions } @@ -494,19 +432,12 @@ func (o *DownloadBufferOptions) format() *blob.DownloadBufferOptions { } downloadBufferOptions.AccessConditions = exported.FormatBlobAccessConditions(o.AccessConditions) - downloadBufferOptions.CPKScopeInfo = (*blob.CPKScopeInfo)(o.CPKScopeInfo) + downloadBufferOptions.CPKScopeInfo = o.CPKScopeInfo downloadBufferOptions.BlockSize = o.ChunkSize downloadBufferOptions.Progress = o.Progress downloadBufferOptions.Concurrency = o.Concurrency if o.RetryReaderOptionsPerChunk != nil { - newFunc := func(failureCount int32, lastError error, rnge blob.HTTPRange, willRetry bool) { - newRange := HTTPRange{ - Offset: rnge.Offset, - Count: rnge.Count, - } - o.RetryReaderOptionsPerChunk.OnFailedRead(failureCount, lastError, newRange, willRetry) - } - downloadBufferOptions.RetryReaderOptionsPerBlock.OnFailedRead = newFunc + downloadBufferOptions.RetryReaderOptionsPerBlock.OnFailedRead = o.RetryReaderOptionsPerChunk.OnFailedRead downloadBufferOptions.RetryReaderOptionsPerBlock.EarlyCloseAsError = o.RetryReaderOptionsPerChunk.EarlyCloseAsError downloadBufferOptions.RetryReaderOptionsPerBlock.MaxRetries = o.RetryReaderOptionsPerChunk.MaxRetries } @@ -560,19 +491,12 @@ func (o *DownloadFileOptions) format() *blob.DownloadFileOptions { } downloadFileOptions.AccessConditions = exported.FormatBlobAccessConditions(o.AccessConditions) - downloadFileOptions.CPKScopeInfo = (*blob.CPKScopeInfo)(o.CPKScopeInfo) + downloadFileOptions.CPKScopeInfo = o.CPKScopeInfo downloadFileOptions.BlockSize = o.ChunkSize downloadFileOptions.Progress = o.Progress downloadFileOptions.Concurrency = o.Concurrency if o.RetryReaderOptionsPerChunk != nil { - newFunc := func(failureCount int32, lastError error, rnge blob.HTTPRange, willRetry bool) { - newRange := HTTPRange{ - Offset: rnge.Offset, - Count: rnge.Count, - } - o.RetryReaderOptionsPerChunk.OnFailedRead(failureCount, lastError, newRange, willRetry) - } - downloadFileOptions.RetryReaderOptionsPerBlock.OnFailedRead = newFunc + downloadFileOptions.RetryReaderOptionsPerBlock.OnFailedRead = o.RetryReaderOptionsPerChunk.OnFailedRead downloadFileOptions.RetryReaderOptionsPerBlock.EarlyCloseAsError = o.RetryReaderOptionsPerChunk.EarlyCloseAsError downloadFileOptions.RetryReaderOptionsPerBlock.MaxRetries = o.RetryReaderOptionsPerChunk.MaxRetries } @@ -640,8 +564,16 @@ type SetExpiryTypeNever = exported.SetExpiryTypeNever // SetExpiryOptions contains the optional parameters for the Client.SetExpiry method. type SetExpiryOptions = exported.SetExpiryOptions +type HTTPRange = exported.HTTPRange + // ================================= path imports ================================== +// DeleteOptions contains the optional parameters when calling the Delete operation. dfs endpoint +type DeleteOptions = path.DeleteOptions + +// RenameOptions contains the optional parameters when calling the Rename operation. +type RenameOptions = path.RenameOptions + // GetPropertiesOptions contains the optional parameters for the Client.GetProperties method type GetPropertiesOptions = path.GetPropertiesOptions @@ -685,4 +617,4 @@ type ModifiedAccessConditions = path.ModifiedAccessConditions type SourceModifiedAccessConditions = path.SourceModifiedAccessConditions // CPKScopeInfo contains a group of parameters for the PathClient.SetMetadata method. -type CPKScopeInfo path.CPKScopeInfo +type CPKScopeInfo = path.CPKScopeInfo diff --git a/sdk/storage/azdatalake/filesystem/client.go b/sdk/storage/azdatalake/filesystem/client.go index c0c7ee5b8086..6c21c0ed364e 100644 --- a/sdk/storage/azdatalake/filesystem/client.go +++ b/sdk/storage/azdatalake/filesystem/client.go @@ -282,7 +282,8 @@ func (fs *Client) NewListPathsPager(recursive bool, options *ListPathsOptions) * if !runtime.HasStatusCode(resp, http.StatusOK) { return ListPathsSegmentResponse{}, runtime.NewResponseError(resp) } - return fs.generatedFSClientWithDFS().ListPathsHandleResponse(resp) + newResp, err := fs.generatedFSClientWithDFS().ListPathsHandleResponse(resp) + return newResp, exported.ConvertToDFSError(err) }, }) } @@ -317,7 +318,8 @@ func (fs *Client) NewListDeletedPathsPager(options *ListDeletedPathsOptions) *ru if !runtime.HasStatusCode(resp, http.StatusOK) { return ListDeletedPathsSegmentResponse{}, runtime.NewResponseError(resp) } - return fs.generatedFSClientWithDFS().ListBlobHierarchySegmentHandleResponse(resp) + newResp, err := fs.generatedFSClientWithDFS().ListBlobHierarchySegmentHandleResponse(resp) + return newResp, exported.ConvertToDFSError(err) }, }) } diff --git a/sdk/storage/azdatalake/internal/exported/exported.go b/sdk/storage/azdatalake/internal/exported/exported.go index ab0c5ebea4cf..0dea8e21223c 100644 --- a/sdk/storage/azdatalake/internal/exported/exported.go +++ b/sdk/storage/azdatalake/internal/exported/exported.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" "strconv" "strings" ) @@ -19,10 +20,7 @@ const SnapshotTimeFormat = "2006-01-02T15:04:05.0000000Z07:00" // 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 = blob.HTTPRange // FormatHTTPRange converts an HTTPRange to its string format. func FormatHTTPRange(r HTTPRange) *string { diff --git a/sdk/storage/azdatalake/internal/generated/autorest.md b/sdk/storage/azdatalake/internal/generated/autorest.md index e5184eb7f1df..260f00f66118 100644 --- a/sdk/storage/azdatalake/internal/generated/autorest.md +++ b/sdk/storage/azdatalake/internal/generated/autorest.md @@ -76,8 +76,23 @@ directive: transform: >- return $. replace(/func \(client \*ServiceClient\) NewListFileSystemsPager\(.+\/\/ listFileSystemsCreateRequest creates the ListFileSystems request/s, `//\n// ListFileSystemsCreateRequest creates the ListFileSystems request`). - replace(/\(client \*ServiceClient\) listFileSystemsCreateRequest\(/, `(client *FileSystemClient) ListFileSystemsCreateRequest(`). - replace(/\(client \*ServiceClient\) listFileSystemsHandleResponse\(/, `(client *FileSystemClient) ListFileSystemsHandleResponse(`); + replace(/\(client \*ServiceClient\) listFileSystemsCreateRequest\(/, `(client *ServiceClient) ListFileSystemsCreateRequest(`). + replace(/\(client \*ServiceClient\) listFileSystemsHandleResponse\(/, `(client *ServiceClient) ListFileSystemsHandleResponse(`); +``` + + +### Remove pager methods and export various generated methods in path client + +``` yaml +directive: + - from: zz_path_client.go + where: $ + transform: >- + return $. + replace(/\(client \*PathClient\) setAccessControlRecursiveCreateRequest\(/, `(client *PathClient) SetAccessControlRecursiveCreateRequest(`). + replace(/\(client \*PathClient\) setAccessControlRecursiveHandleResponse\(/, `(client *PathClient) SetAccessControlRecursiveHandleResponse(`). + replace(/setAccessControlRecursiveCreateRequest/g, 'SetAccessControlRecursiveCreateRequest'). + replace(/setAccessControlRecursiveHandleResponse/g, 'SetAccessControlRecursiveHandleResponse'); ``` ### Fix EncryptionAlgorithm diff --git a/sdk/storage/azdatalake/internal/generated/zz_path_client.go b/sdk/storage/azdatalake/internal/generated/zz_path_client.go index 4f3c3b3ba19f..f0547cb91e1a 100644 --- a/sdk/storage/azdatalake/internal/generated/zz_path_client.go +++ b/sdk/storage/azdatalake/internal/generated/zz_path_client.go @@ -1124,7 +1124,7 @@ func (client *PathClient) setAccessControlHandleResponse(resp *http.Response) (P // - options - PathClientSetAccessControlRecursiveOptions contains the optional parameters for the PathClient.SetAccessControlRecursive // method. func (client *PathClient) SetAccessControlRecursive(ctx context.Context, mode PathSetAccessControlRecursiveMode, options *PathClientSetAccessControlRecursiveOptions) (PathClientSetAccessControlRecursiveResponse, error) { - req, err := client.setAccessControlRecursiveCreateRequest(ctx, mode, options) + req, err := client.SetAccessControlRecursiveCreateRequest(ctx, mode, options) if err != nil { return PathClientSetAccessControlRecursiveResponse{}, err } @@ -1135,11 +1135,11 @@ func (client *PathClient) SetAccessControlRecursive(ctx context.Context, mode Pa if !runtime.HasStatusCode(resp, http.StatusOK) { return PathClientSetAccessControlRecursiveResponse{}, runtime.NewResponseError(resp) } - return client.setAccessControlRecursiveHandleResponse(resp) + return client.SetAccessControlRecursiveHandleResponse(resp) } -// setAccessControlRecursiveCreateRequest creates the SetAccessControlRecursive request. -func (client *PathClient) setAccessControlRecursiveCreateRequest(ctx context.Context, mode PathSetAccessControlRecursiveMode, options *PathClientSetAccessControlRecursiveOptions) (*policy.Request, error) { +// SetAccessControlRecursiveCreateRequest creates the SetAccessControlRecursive request. +func (client *PathClient) SetAccessControlRecursiveCreateRequest(ctx context.Context, mode PathSetAccessControlRecursiveMode, options *PathClientSetAccessControlRecursiveOptions) (*policy.Request, error) { req, err := runtime.NewRequest(ctx, http.MethodPatch, client.endpoint) if err != nil { return nil, err @@ -1171,8 +1171,8 @@ func (client *PathClient) setAccessControlRecursiveCreateRequest(ctx context.Con return req, nil } -// setAccessControlRecursiveHandleResponse handles the SetAccessControlRecursive response. -func (client *PathClient) setAccessControlRecursiveHandleResponse(resp *http.Response) (PathClientSetAccessControlRecursiveResponse, error) { +// SetAccessControlRecursiveHandleResponse handles the SetAccessControlRecursive response. +func (client *PathClient) SetAccessControlRecursiveHandleResponse(resp *http.Response) (PathClientSetAccessControlRecursiveResponse, error) { result := PathClientSetAccessControlRecursiveResponse{} if val := resp.Header.Get("Date"); val != "" { date, err := time.Parse(time.RFC1123, val) diff --git a/sdk/storage/azdatalake/internal/generated/zz_service_client.go b/sdk/storage/azdatalake/internal/generated/zz_service_client.go index f4322dd4f3fd..4c6e81408b18 100644 --- a/sdk/storage/azdatalake/internal/generated/zz_service_client.go +++ b/sdk/storage/azdatalake/internal/generated/zz_service_client.go @@ -34,7 +34,7 @@ type ServiceClient struct { // method. // // ListFileSystemsCreateRequest creates the ListFileSystems request. -func (client *FileSystemClient) ListFileSystemsCreateRequest(ctx context.Context, options *ServiceClientListFileSystemsOptions) (*policy.Request, error) { +func (client *ServiceClient) ListFileSystemsCreateRequest(ctx context.Context, options *ServiceClientListFileSystemsOptions) (*policy.Request, error) { req, err := runtime.NewRequest(ctx, http.MethodGet, client.endpoint) if err != nil { return nil, err @@ -63,7 +63,7 @@ func (client *FileSystemClient) ListFileSystemsCreateRequest(ctx context.Context } // listFileSystemsHandleResponse handles the ListFileSystems response. -func (client *FileSystemClient) ListFileSystemsHandleResponse(resp *http.Response) (ServiceClientListFileSystemsResponse, error) { +func (client *ServiceClient) ListFileSystemsHandleResponse(resp *http.Response) (ServiceClientListFileSystemsResponse, error) { result := ServiceClientListFileSystemsResponse{} if val := resp.Header.Get("Date"); val != "" { date, err := time.Parse(time.RFC1123, val) diff --git a/sdk/storage/azdatalake/internal/path/models.go b/sdk/storage/azdatalake/internal/path/models.go index f476fcae518f..112dda4c922c 100644 --- a/sdk/storage/azdatalake/internal/path/models.go +++ b/sdk/storage/azdatalake/internal/path/models.go @@ -15,6 +15,59 @@ import ( "time" ) +// DeleteOptions contains the optional parameters when calling the Delete operation. dfs endpoint +type DeleteOptions struct { + // AccessConditions contains parameters for accessing the file. + AccessConditions *AccessConditions +} + +func FormatDeleteOptions(o *DeleteOptions, recursive bool) (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.PathClientDeleteOptions) { + deleteOpts := &generated.PathClientDeleteOptions{ + Recursive: &recursive, + } + if o == nil { + return nil, nil, deleteOpts + } + leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) + return leaseAccessConditions, modifiedAccessConditions, deleteOpts +} + +// RenameOptions contains the optional parameters when calling the Rename operation. +type RenameOptions struct { + // SourceAccessConditions identifies the source path access conditions. + SourceAccessConditions *SourceAccessConditions + // AccessConditions contains parameters for accessing the file. + AccessConditions *AccessConditions +} + +func FormatRenameOptions(o *RenameOptions, path string) (*generated.LeaseAccessConditions, *generated.ModifiedAccessConditions, *generated.SourceModifiedAccessConditions, *generated.PathClientCreateOptions) { + // we don't need sourceModAccCond since this is not rename + mode := generated.PathRenameModeLegacy + createOpts := &generated.PathClientCreateOptions{ + Mode: &mode, + RenameSource: &path, + } + if o == nil { + return nil, nil, nil, createOpts + } + leaseAccessConditions, modifiedAccessConditions := exported.FormatPathAccessConditions(o.AccessConditions) + if o.SourceAccessConditions != nil { + if o.SourceAccessConditions.SourceLeaseAccessConditions != nil { + createOpts.SourceLeaseID = o.SourceAccessConditions.SourceLeaseAccessConditions.LeaseID + } + if o.SourceAccessConditions.SourceModifiedAccessConditions != nil { + sourceModifiedAccessConditions := &generated.SourceModifiedAccessConditions{ + SourceIfMatch: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfMatch, + SourceIfModifiedSince: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfModifiedSince, + SourceIfNoneMatch: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfNoneMatch, + SourceIfUnmodifiedSince: o.SourceAccessConditions.SourceModifiedAccessConditions.SourceIfUnmodifiedSince, + } + return leaseAccessConditions, modifiedAccessConditions, sourceModifiedAccessConditions, createOpts + } + } + return leaseAccessConditions, modifiedAccessConditions, nil, createOpts +} + // GetPropertiesOptions contains the optional parameters for the Client.GetProperties method type GetPropertiesOptions struct { AccessConditions *AccessConditions @@ -173,7 +226,6 @@ func FormatBlobHTTPHeaders(o *HTTPHeaders) *blob.HTTPHeaders { } func FormatPathHTTPHeaders(o *HTTPHeaders) *generated.PathHTTPHeaders { - // TODO: will be used for file related ops, like append if o == nil { return nil } @@ -239,4 +291,4 @@ type ModifiedAccessConditions = exported.ModifiedAccessConditions type SourceModifiedAccessConditions = exported.SourceModifiedAccessConditions // CPKScopeInfo contains a group of parameters for the Client.SetMetadata() method. -type CPKScopeInfo blob.CPKScopeInfo +type CPKScopeInfo = blob.CPKScopeInfo diff --git a/sdk/storage/azdatalake/internal/testcommon/clients_auth.go b/sdk/storage/azdatalake/internal/testcommon/clients_auth.go index bd1607b2fec4..20c2d683daa5 100644 --- a/sdk/storage/azdatalake/internal/testcommon/clients_auth.go +++ b/sdk/storage/azdatalake/internal/testcommon/clients_auth.go @@ -8,6 +8,7 @@ import ( "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/azdatalake" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/directory" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/file" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/filesystem" "github.com/Azure/azure-sdk-for-go/sdk/storage/azdatalake/service" @@ -140,6 +141,23 @@ func GetFileClient(fsName, fName string, t *testing.T, accountType TestAccountTy return fileClient, err } +func GetDirClient(fsName, dirName string, t *testing.T, accountType TestAccountType, options *directory.ClientOptions) (*directory.Client, error) { + if options == nil { + options = &directory.ClientOptions{} + } + + SetClientOptions(t, &options.ClientOptions) + + cred, err := GetGenericSharedKeyCredential(accountType) + if err != nil { + return nil, err + } + + dirClient, err := directory.NewClientWithSharedKeyCredential("https://"+cred.AccountName()+".dfs.core.windows.net/"+fsName+"/"+dirName, cred, options) + + return dirClient, err +} + func ServiceGetFilesystemClient(filesystemName string, s *service.Client) *filesystem.Client { return s.NewFilesystemClient(filesystemName) } @@ -154,6 +172,11 @@ func DeleteFile(ctx context.Context, _require *require.Assertions, fileClient *f _require.Nil(err) } +func DeleteDir(ctx context.Context, _require *require.Assertions, dirClient *directory.Client) { + _, err := dirClient.Delete(ctx, nil) + _require.Nil(err) +} + func GetGenericConnectionString(accountType TestAccountType) (*string, error) { accountName, accountKey := GetGenericAccountInfo(accountType) if accountName == "" || accountKey == "" { diff --git a/sdk/storage/azdatalake/internal/testcommon/common.go b/sdk/storage/azdatalake/internal/testcommon/common.go index 36af75cc92a7..e5451ba6a38b 100644 --- a/sdk/storage/azdatalake/internal/testcommon/common.go +++ b/sdk/storage/azdatalake/internal/testcommon/common.go @@ -18,6 +18,7 @@ import ( const ( FilesystemPrefix = "gofs" FilePrefix = "gotestfile" + DirPrefix = "gotestdir" DefaultData = "Godatalakedata" InvalidHeaderErrorSubstring = "invalid header field" // error thrown by the http client ) @@ -30,6 +31,10 @@ func GenerateFileName(testName string) string { return FilePrefix + GenerateEntityName(testName) } +func GenerateDirName(testName string) string { + return DirPrefix + GenerateEntityName(testName) +} + func GenerateEntityName(testName string) string { return strings.ReplaceAll(strings.ReplaceAll(strings.ToLower(testName), "/", ""), "test", "") }