Added support for create/delete file shares. (#382)

* Added support for create/delete file shares.

* Removed nullable for metadata.
This commit is contained in:
Kan Tang 2020-08-04 09:54:04 +08:00 committed by GitHub
parent 8867c7abcb
commit 3de57667d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 800 additions and 442 deletions

View File

@ -146,10 +146,12 @@ set (AZURE_STORAGE_SHARES_HEADER
inc/shares/share_options.hpp
inc/shares/share_responses.hpp
inc/shares/service_client.hpp
inc/shares/share_client.hpp
)
set (AZURE_STORAGE_SHARES_SOURCE
src/shares/service_client.cpp
src/shares/share_client.cpp
)
add_library(azure-storage-file-share ${AZURE_STORAGE_SHARES_HEADER} ${AZURE_STORAGE_SHARES_SOURCE})

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@
namespace Azure { namespace Storage { namespace Files { namespace Shares {
class ShareClient;
class ServiceClient {
public:
/**
@ -60,6 +62,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
const std::string& serviceUri,
const ServiceClientOptions& options = ServiceClientOptions());
/**
* @brief Create a ShareClient from current ServiceClient
* @param shareName The name of the file share.
* @return ShareClient A share client that can be used to manage a share resource.
*/
ShareClient GetShareClient(const std::string& shareName) const;
/**
* @brief Gets the file share service's primary uri endpoint.
*
@ -70,8 +79,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
/**
* @brief List the shares from the service.
* @param options Optional parameters to list the shares.
* @return Azure::Core::Response<ListSharesResult> The results containing the shares returned
* and information used for future list operation on valid result not yet returned.
* @return Azure::Core::Response<ListSharesSegmentResult> The results containing the shares
* returned and information used for future list operation on valid result not yet returned.
*/
Azure::Core::Response<ListSharesSegmentResult> ListSharesSegment(
const ListSharesOptions& options = ListSharesOptions()) const;

View File

@ -0,0 +1,102 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include "common/storage_credential.hpp"
#include "common/storage_uri_builder.hpp"
#include "credentials/credentials.hpp"
#include "http/pipeline.hpp"
#include "protocol/share_rest_client.hpp"
#include "response.hpp"
#include "share_options.hpp"
#include "share_responses.hpp"
#include "shares/service_client.hpp"
#include <memory>
#include <string>
namespace Azure { namespace Storage { namespace Files { namespace Shares {
class ShareClient {
public:
/**
* @brief Create from connection string
* @param connectionString Azure Storage connection string.
* @param shareName The name of a file share.
* @param options Optional parameters used to initialize the client.
* @return ShareClient The client that can be used to manage a share resource.
*/
static ShareClient CreateFromConnectionString(
const std::string& connectionString,
const std::string& shareName,
const ShareClientOptions& options = ShareClientOptions());
/**
* @brief Shared key authentication client.
* @param shareUri The URI of the file share this client's request targets.
* @param credential The shared key credential used to initialize the client.
* @param options Optional parameters used to initialize the client.
*/
explicit ShareClient(
const std::string& shareUri,
std::shared_ptr<SharedKeyCredential> credential,
const ShareClientOptions& options = ShareClientOptions());
/**
* @brief Bearer token authentication client.
* @param shareUri The URI of the file share this client's request targets.
* @param credential The token credential used to initialize the client.
* @param options Optional parameters used to initialize the client.
*/
explicit ShareClient(
const std::string& shareUri,
std::shared_ptr<Core::Credentials::TokenCredential> credential,
const ShareClientOptions& options = ShareClientOptions());
/**
* @brief Anonymous/SAS/customized pipeline auth.
* @param shareUri The URI of the file share this client's request targets.
* @param options Optional parameters used to initialize the client.
*/
explicit ShareClient(
const std::string& shareUri,
const ShareClientOptions& options = ShareClientOptions());
/**
* @brief Gets the share's primary uri endpoint.
*
* @return The share's primary uri endpoint.
*/
std::string GetUri() const { return m_shareUri.ToString(); }
/**
* @brief Creates the file share.
* @param options Optional parameters to create this file share.
* @return Azure::Core::Response<ShareInfo> The information containing the version and modified
* time of a share.
*/
Azure::Core::Response<ShareInfo> Create(
const CreateShareOptions& options = CreateShareOptions()) const;
/**
* @brief Deletes the file share.
* @param options Optional parameters to delete this file share.
* @return Azure::Core::Response<ShareDeleteInfo> Currently empty and reserved for future usage.
*/
Azure::Core::Response<ShareDeleteInfo> Delete(
const DeleteShareOptions& options = DeleteShareOptions()) const;
private:
UriBuilder m_shareUri;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
explicit ShareClient(
UriBuilder shareUri,
std::shared_ptr<Azure::Core::Http::HttpPipeline> pipeline)
: m_shareUri(std::move(shareUri)), m_pipeline(std::move(pipeline))
{
}
friend class ServiceClient;
};
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -23,6 +23,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> PerRetryPolicies;
};
/**
* @brief Share client options used to initalize ShareClient.
*/
struct ShareClientOptions
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> PerOperationPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> PerRetryPolicies;
};
struct ListSharesOptions
{
/**
@ -57,4 +66,41 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Nullable<ListSharesIncludeType> ListSharesInclude;
};
struct CreateShareOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
/**
* @brief A name-value pair to associate with a file storage object.
*/
std::map<std::string, std::string> Metadata;
/**
* @brief Specifies the maximum size of the share, in gigabytes.
*/
Azure::Core::Nullable<int32_t> ShareQuota;
};
struct DeleteShareOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
/**
* @brief The snapshot parameter is an opaque DateTime value that, when present, specifies the
* share snapshot to query.
*/
Azure::Core::Nullable<std::string> ShareSnapshot;
/**
* @brief Specifies the option include to delete the base share and all of its snapshots.
*/
Azure::Core::Nullable<bool> IncludeSnapshots;
};
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -11,4 +11,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
using ListSharesSegmentResult = ServiceListSharesSegmentResponse;
// ShareClient models:
using ShareInfo = ShareCreateResponse;
using ShareDeleteInfo = ShareDeleteResponse;
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -3,4 +3,5 @@
#pragma once
#include "service_client.hpp"
#include "service_client.hpp"
#include "share_client.hpp"

View File

@ -11,6 +11,7 @@
#include "common/storage_version.hpp"
#include "credentials/policy/policies.hpp"
#include "http/curl/curl.hpp"
#include "shares/share_client.hpp"
namespace Azure { namespace Storage { namespace Files { namespace Shares {
ServiceClient ServiceClient::CreateFromConnectionString(
@ -18,7 +19,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
const ServiceClientOptions& options)
{
auto parsedConnectionString = Azure::Storage::Details::ParseConnectionString(connectionString);
auto serviceUri = std::move(parsedConnectionString.DataLakeServiceUri);
auto serviceUri = std::move(parsedConnectionString.FileServiceUri);
if (parsedConnectionString.KeyCredential)
{
@ -106,14 +107,21 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
ShareClient ServiceClient::GetShareClient(const std::string& shareName) const
{
auto builder = m_serviceUri;
builder.AppendPath(shareName, true);
return ShareClient(builder, m_pipeline);
}
Azure::Core::Response<ListSharesSegmentResult> ServiceClient::ListSharesSegment(
const ListSharesOptions& options) const
{
auto protocolLayerOptions = ShareRestClient::Service::ListSharesSegmentOptions();
protocolLayerOptions.ListSharesInclude = std::move(options.ListSharesInclude);
protocolLayerOptions.Marker = std::move(options.Marker);
protocolLayerOptions.MaxResults = std::move(options.MaxResults);
protocolLayerOptions.Prefix = std::move(options.Prefix);
protocolLayerOptions.ListSharesInclude = options.ListSharesInclude;
protocolLayerOptions.Marker = options.Marker;
protocolLayerOptions.MaxResults = options.MaxResults;
protocolLayerOptions.Prefix = options.Prefix;
return ShareRestClient::Service::ListSharesSegment(
m_serviceUri.ToString(), *m_pipeline, options.Context, protocolLayerOptions);
}

View File

@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "shares/share_client.hpp"
#include "common/common_headers_request_policy.hpp"
#include "common/constants.hpp"
#include "common/crypt.hpp"
#include "common/shared_key_policy.hpp"
#include "common/storage_common.hpp"
#include "common/storage_version.hpp"
#include "credentials/policy/policies.hpp"
#include "http/curl/curl.hpp"
namespace Azure { namespace Storage { namespace Files { namespace Shares {
ShareClient ShareClient::CreateFromConnectionString(
const std::string& connectionString,
const std::string& shareName,
const ShareClientOptions& options)
{
auto parsedConnectionString = Azure::Storage::Details::ParseConnectionString(connectionString);
auto shareUri = std::move(parsedConnectionString.FileServiceUri);
shareUri.AppendPath(shareName, true);
if (parsedConnectionString.KeyCredential)
{
return ShareClient(shareUri.ToString(), parsedConnectionString.KeyCredential, options);
}
else
{
return ShareClient(shareUri.ToString(), options);
}
}
ShareClient::ShareClient(
const std::string& shareUri,
std::shared_ptr<SharedKeyCredential> credential,
const ShareClientOptions& options)
: m_shareUri(shareUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Azure::Storage::Details::c_FileServicePackageName, FileServiceVersion));
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Azure::Core::Http::RetryPolicy>(Azure::Core::Http::RetryOptions()));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<SharedKeyPolicy>(credential));
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
ShareClient::ShareClient(
const std::string& shareUri,
std::shared_ptr<Core::Credentials::TokenCredential> credential,
const ShareClientOptions& options)
: m_shareUri(shareUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Azure::Storage::Details::c_FileServicePackageName, FileServiceVersion));
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Azure::Core::Http::RetryPolicy>(Azure::Core::Http::RetryOptions()));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(
std::make_unique<Core::Credentials::Policy::BearerTokenAuthenticationPolicy>(
credential, Azure::Storage::Details::c_StorageScope));
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
ShareClient::ShareClient(const std::string& shareUri, const ShareClientOptions& options)
: m_shareUri(shareUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Azure::Storage::Details::c_FileServicePackageName, FileServiceVersion));
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Azure::Core::Http::RetryPolicy>(Azure::Core::Http::RetryOptions()));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
Azure::Core::Response<ShareInfo> ShareClient::Create(const CreateShareOptions& options) const
{
auto protocolLayerOptions = ShareRestClient::Share::CreateOptions();
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.ShareQuota = options.ShareQuota;
return ShareRestClient::Share::Create(
m_shareUri.ToString(), *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<ShareDeleteResponse> ShareClient::Delete(
const DeleteShareOptions& options) const
{
auto protocolLayerOptions = ShareRestClient::Share::DeleteOptions();
protocolLayerOptions.ShareSnapshot = options.ShareSnapshot;
if (options.IncludeSnapshots.HasValue() and options.IncludeSnapshots.GetValue())
{
protocolLayerOptions.XMsDeleteSnapshots = DeleteSnapshotsOptionType::Include;
}
return ShareRestClient::Share::Delete(
m_shareUri.ToString(), *m_pipeline, options.Context, protocolLayerOptions);
}
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -30,11 +30,15 @@ add_executable (
datalake/directory_client_test.hpp
datalake/directory_client_test.cpp
common/bearer_token_test.cpp
)
shares/service_client_test.hpp
shares/service_client_test.cpp
shares/share_client_test.cpp
shares/share_client_test.hpp
)
target_include_directories(azure-storage-test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(azure-storage-test PUBLIC azure::storage::blob azure::storage::file::datalake)
target_link_libraries(azure-storage-test PUBLIC azure::storage::blob azure::storage::file::datalake azure::storage::file::share)
if (MSVC)
target_compile_options(azure-storage-test PUBLIC /wd6326 /wd26495 /wd26812)

View File

@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "service_client_test.hpp"
#include <algorithm>
namespace Azure { namespace Storage { namespace Test {
const size_t c_SHARE_TEST_SIZE = 5;
std::shared_ptr<Files::Shares::ServiceClient>
FileShareServiceClientTest::m_fileShareServiceClient;
std::vector<std::string> FileShareServiceClientTest::m_shareNameSetA;
std::vector<std::string> FileShareServiceClientTest::m_shareNameSetB;
std::string FileShareServiceClientTest::m_sharePrefixA;
std::string FileShareServiceClientTest::m_sharePrefixB;
void FileShareServiceClientTest::SetUpTestSuite()
{
m_fileShareServiceClient = std::make_shared<Files::Shares::ServiceClient>(
Files::Shares::ServiceClient::CreateFromConnectionString(
StandardStorageConnectionString()));
m_sharePrefixA = LowercaseRandomString(10);
m_sharePrefixB = LowercaseRandomString(10);
for (size_t i = 0; i < c_SHARE_TEST_SIZE; ++i)
{
{
auto name = m_sharePrefixA + LowercaseRandomString(10);
m_fileShareServiceClient->GetShareClient(name).Create();
m_shareNameSetA.emplace_back(std::move(name));
}
{
auto name = m_sharePrefixB + LowercaseRandomString(10);
m_fileShareServiceClient->GetShareClient(name).Create();
m_shareNameSetB.emplace_back(std::move(name));
}
}
}
void FileShareServiceClientTest::TearDownTestSuite()
{
for (const auto& name : m_shareNameSetA)
{
m_fileShareServiceClient->GetShareClient(name).Delete();
}
for (const auto& name : m_shareNameSetB)
{
m_fileShareServiceClient->GetShareClient(name).Delete();
}
}
std::vector<Files::Shares::ShareItem> FileShareServiceClientTest::ListAllShares(
const std::string& prefix)
{
std::vector<Files::Shares::ShareItem> result;
std::string continuation;
Files::Shares::ListSharesOptions options;
if (!prefix.empty())
{
options.Prefix = prefix;
}
do
{
auto response = m_fileShareServiceClient->ListSharesSegment(options);
result.insert(result.end(), response->ShareItems.begin(), response->ShareItems.end());
continuation = response->NextMarker;
options.Marker = continuation;
} while (!continuation.empty());
return result;
}
TEST_F(FileShareServiceClientTest, ListShares)
{
{
// Normal list without prefix.
auto result = ListAllShares();
for (const auto& name : m_shareNameSetA)
{
auto iter = std::find_if(
result.begin(), result.end(), [&name](const Files::Shares::ShareItem& share) {
return share.Name == name;
});
EXPECT_EQ(iter->Name.substr(0U, m_sharePrefixA.size()), m_sharePrefixA);
EXPECT_NE(result.end(), iter);
}
for (const auto& name : m_shareNameSetB)
{
auto iter = std::find_if(
result.begin(), result.end(), [&name](const Files::Shares::ShareItem& share) {
return share.Name == name;
});
EXPECT_EQ(iter->Name.substr(0U, m_sharePrefixB.size()), m_sharePrefixB);
EXPECT_NE(result.end(), iter);
}
}
{
// List prefix.
auto result = ListAllShares(m_sharePrefixA);
for (const auto& name : m_shareNameSetA)
{
auto iter = std::find_if(
result.begin(), result.end(), [&name](const Files::Shares::ShareItem& share) {
return share.Name == name;
});
EXPECT_EQ(iter->Name.substr(0U, m_sharePrefixA.size()), m_sharePrefixA);
EXPECT_NE(result.end(), iter);
}
for (const auto& name : m_shareNameSetB)
{
auto iter = std::find_if(
result.begin(), result.end(), [&name](const Files::Shares::ShareItem& share) {
return share.Name == name;
});
EXPECT_EQ(result.end(), iter);
}
}
{
// List max result
Files::Shares::ListSharesOptions options;
options.MaxResults = 2;
auto response = m_fileShareServiceClient->ListSharesSegment(options);
EXPECT_LE(2U, response->ShareItems.size());
}
}
}}} // namespace Azure::Storage::Test

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "shares/shares.hpp"
#include "test_base.hpp"
namespace Azure { namespace Storage { namespace Test {
class FileShareServiceClientTest : public ::testing::Test {
protected:
static void SetUpTestSuite();
static void TearDownTestSuite();
static std::vector<Files::Shares::ShareItem> ListAllShares(
const std::string& prefix = std::string());
static std::shared_ptr<Files::Shares::ServiceClient> m_fileShareServiceClient;
static std::vector<std::string> m_shareNameSetA;
static std::string m_sharePrefixA;
static std::vector<std::string> m_shareNameSetB;
static std::string m_sharePrefixB;
};
}}} // namespace Azure::Storage::Test

View File

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "share_client_test.hpp"
#include <algorithm>
namespace Azure { namespace Storage { namespace Test {
std::shared_ptr<Files::Shares::ShareClient> FileShareClientTest::m_shareClient;
std::string FileShareClientTest::m_fileSystemName;
void FileShareClientTest::SetUpTestSuite()
{
m_fileSystemName = LowercaseRandomString();
m_shareClient = std::make_shared<Files::Shares::ShareClient>(
Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_fileSystemName));
m_shareClient->Create();
}
void FileShareClientTest::TearDownTestSuite() { m_shareClient->Delete(); }
Files::Shares::FileShareHttpHeaders FileShareClientTest::GetInterestingHttpHeaders()
{
static Files::Shares::FileShareHttpHeaders result = []() {
Files::Shares::FileShareHttpHeaders ret;
ret.CacheControl = std::string("no-cache");
ret.ContentDisposition = std::string("attachment");
ret.ContentEncoding = std::string("deflate");
ret.ContentLanguage = std::string("en-US");
ret.ContentType = std::string("application/octet-stream");
return ret;
}();
return result;
}
TEST_F(FileShareClientTest, CreateDeleteFileSystems)
{
{
// Normal create/delete.
std::vector<Files::Shares::ShareClient> fileSystemClient;
for (int32_t i = 0; i < 5; ++i)
{
auto client = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
EXPECT_NO_THROW(client.Create());
fileSystemClient.emplace_back(std::move(client));
}
for (const auto& client : fileSystemClient)
{
EXPECT_NO_THROW(client.Delete());
}
}
}
}}} // namespace Azure::Storage::Test

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "shares/shares.hpp"
#include "test_base.hpp"
namespace Azure { namespace Storage { namespace Test {
class FileShareClientTest : public ::testing::Test {
protected:
static void SetUpTestSuite();
static void TearDownTestSuite();
static Files::Shares::FileShareHttpHeaders GetInterestingHttpHeaders();
static std::shared_ptr<Files::Shares::ShareClient> m_shareClient;
static std::string m_fileSystemName;
};
}}} // namespace Azure::Storage::Test