Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Container soft delete #19136

Merged
merged 4 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions sdk/storage/azblob/container/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ func (c *Client) Delete(ctx context.Context, options *DeleteOptions) (DeleteResp
return resp, err
}

// Restore operation restore the contents and properties of a soft deleted container to a specified container.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/restore-container.
func (c *Client) Restore(ctx context.Context, deletedContainerVersion string, options *RestoreOptions) (RestoreResponse, error) {
urlParts, err := blob.ParseURL(c.URL())
if err != nil {
return RestoreResponse{}, err
}

opts := &generated.ContainerClientRestoreOptions{
souravgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
DeletedContainerName: &urlParts.ContainerName,
DeletedContainerVersion: &deletedContainerVersion,
}
resp, err := c.generated().Restore(ctx, opts)

return resp, err
}

// GetProperties returns the container's properties.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-container-metadata.
func (c *Client) GetProperties(ctx context.Context, o *GetPropertiesOptions) (GetPropertiesResponse, error) {
Expand Down
7 changes: 7 additions & 0 deletions sdk/storage/azblob/container/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ func (o *DeleteOptions) format() (*generated.ContainerClientDeleteOptions, *gene

// ---------------------------------------------------------------------------------------------------------------------

// RestoreOptions contains the optional parameters for the Client.Restore method.
type RestoreOptions struct {
// placeholder for future options
}

// ---------------------------------------------------------------------------------------------------------------------

// GetPropertiesOptions contains the optional parameters for the ContainerClient.GetProperties method.
type GetPropertiesOptions struct {
LeaseAccessConditions *LeaseAccessConditions
Expand Down
3 changes: 3 additions & 0 deletions sdk/storage/azblob/container/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ type CreateResponse = generated.ContainerClientCreateResponse
// DeleteResponse contains the response from method Client.Delete.
type DeleteResponse = generated.ContainerClientDeleteResponse

// RestoreResponse contains the response from method Client.Restore.
type RestoreResponse = generated.ContainerClientRestoreResponse

// GetPropertiesResponse contains the response from method Client.GetProperties.
type GetPropertiesResponse = generated.ContainerClientGetPropertiesResponse

Expand Down
8 changes: 8 additions & 0 deletions sdk/storage/azblob/service/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ func (s *Client) DeleteContainer(ctx context.Context, containerName string, opti
return containerDeleteResp, err
}

// RestoreContainer restores soft-deleted container
// Operation will only be successful if used within the specified number of days set in the delete retention policy
func (s *Client) RestoreContainer(ctx context.Context, deletedContainerName string, deletedContainerVersion string, options *RestoreContainerOptions) (RestoreContainerResponse, error) {
containerClient := s.NewContainerClient(deletedContainerName)
containerRestoreResp, err := containerClient.Restore(ctx, deletedContainerVersion, options)
return containerRestoreResp, err
}

// GetAccountInfo provides account level information
func (s *Client) GetAccountInfo(ctx context.Context, o *GetAccountInfoOptions) (GetAccountInfoResponse, error) {
getAccountInfoOptions := o.format()
Expand Down
61 changes: 61 additions & 0 deletions sdk/storage/azblob/service/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,64 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() {
//_, err = containerClient2.Create(ctx, nil)
//_require.Nil(err)
}

// make sure that container soft delete is enabled
// TODO: convert this test to recorded
func (s *ServiceUnrecordedTestsSuite) TestContainerRestore() {
_require := require.New(s.T())
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

testName := s.T().Name()
containerName := testcommon.GenerateContainerName(testName)

_, err = svcClient.CreateContainer(context.Background(), containerName, nil)
_require.Nil(err)

_, err = svcClient.DeleteContainer(context.Background(), containerName, nil)
_require.Nil(err)

prefix := testcommon.ContainerPrefix
listOptions := service.ListContainersOptions{Prefix: &prefix, Include: service.ListContainersInclude{Metadata: true, Deleted: true}}
pager := svcClient.NewListContainersPager(&listOptions)

contRestored := false
for pager.More() {
resp, err := pager.NextPage(context.Background())
_require.Nil(err)
for _, cont := range resp.ContainerItems {
_require.NotNil(cont.Name)

if *cont.Deleted && *cont.Name == containerName {
contRestored = true
_, err = svcClient.RestoreContainer(context.Background(), containerName, *cont.Version, nil)
_require.Nil(err)
break
}
}
if contRestored {
break
}
}

_require.Equal(contRestored, true)

_, err = svcClient.DeleteContainer(context.Background(), containerName, nil)
_require.Nil(err)
}

// TODO: convert this test to recorded
func (s *ServiceUnrecordedTestsSuite) TestContainerRestoreFailures() {
_require := require.New(s.T())
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

testName := s.T().Name()
containerName := testcommon.GenerateContainerName(testName)

_, err = svcClient.RestoreContainer(context.Background(), containerName, "", nil)
testcommon.ValidateBlobErrorCode(_require, err, bloberror.MissingRequiredHeader)

_, err = svcClient.RestoreContainer(context.Background(), "", "", &service.RestoreContainerOptions{})
testcommon.ValidateBlobErrorCode(_require, err, bloberror.MissingRequiredHeader)
}
34 changes: 34 additions & 0 deletions sdk/storage/azblob/service/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,40 @@ func Example_service_Client_DeleteContainer() {
handleError(err)
}

func Example_service_Client_RestoreContainer() {
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME")
if !ok {
panic("AZURE_STORAGE_ACCOUNT_NAME could not be found")
}
serviceURL := fmt.Sprintf("https://%s.blob.core.windows.net/", accountName)

cred, err := azidentity.NewDefaultAzureCredential(nil)
handleError(err)
serviceClient, err := service.NewClient(serviceURL, cred, nil)
handleError(err)

listOptions := service.ListContainersOptions{
Include: service.ListContainersInclude{
Metadata: true, // Include Metadata
Deleted: true, // Include deleted containers in the result as well
},
}
pager := serviceClient.NewListContainersPager(&listOptions)

for pager.More() {
resp, err := pager.NextPage(context.TODO())
if err != nil {
log.Fatal(err)
}
for _, cont := range resp.ContainerItems {
if *cont.Deleted {
_, err = serviceClient.RestoreContainer(context.TODO(), *cont.Name, *cont.Version, nil)
handleError(err)
}
}
}
}

func Example_service_Client_ListContainers() {
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME")
if !ok {
Expand Down
3 changes: 3 additions & 0 deletions sdk/storage/azblob/service/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type CreateContainerOptions = container.CreateOptions
// DeleteContainerOptions contains the optional parameters for the container.Client.Delete method.
type DeleteContainerOptions = container.DeleteOptions

// RestoreContainerOptions contains the optional parameters for the container.Client.Restore method.
type RestoreContainerOptions = container.RestoreOptions

// CorsRule - CORS is an HTTP feature that enables a web application running under one domain to access resources in another
// domain. Web browsers implement a security restriction known as same-origin policy that
// prevents a web page from calling APIs in a different domain; CORS provides a secure way to allow one domain (the origin
Expand Down
3 changes: 3 additions & 0 deletions sdk/storage/azblob/service/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ type CreateContainerResponse = generated.ContainerClientCreateResponse
// DeleteContainerResponse contains the response from method container.Client.Delete
type DeleteContainerResponse = generated.ContainerClientDeleteResponse

// RestoreContainerResponse contains the response from method container.Client.Restore
type RestoreContainerResponse = generated.ContainerClientRestoreResponse

// GetAccountInfoResponse contains the response from method Client.GetAccountInfo.
type GetAccountInfoResponse = generated.ServiceClientGetAccountInfoResponse

Expand Down