Skip to content

Commit 81279eb

Browse files
Container soft delete (Azure#19136)
Adding code for container soft delete
1 parent fedb429 commit 81279eb

File tree

8 files changed

+136
-0
lines changed

8 files changed

+136
-0
lines changed

sdk/storage/azblob/container/client.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,23 @@ func (c *Client) Delete(ctx context.Context, options *DeleteOptions) (DeleteResp
160160
return resp, err
161161
}
162162

163+
// Restore operation restore the contents and properties of a soft deleted container to a specified container.
164+
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/restore-container.
165+
func (c *Client) Restore(ctx context.Context, deletedContainerVersion string, options *RestoreOptions) (RestoreResponse, error) {
166+
urlParts, err := blob.ParseURL(c.URL())
167+
if err != nil {
168+
return RestoreResponse{}, err
169+
}
170+
171+
opts := &generated.ContainerClientRestoreOptions{
172+
DeletedContainerName: &urlParts.ContainerName,
173+
DeletedContainerVersion: &deletedContainerVersion,
174+
}
175+
resp, err := c.generated().Restore(ctx, opts)
176+
177+
return resp, err
178+
}
179+
163180
// GetProperties returns the container's properties.
164181
// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-container-metadata.
165182
func (c *Client) GetProperties(ctx context.Context, o *GetPropertiesOptions) (GetPropertiesResponse, error) {

sdk/storage/azblob/container/models.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ func (o *DeleteOptions) format() (*generated.ContainerClientDeleteOptions, *gene
8484

8585
// ---------------------------------------------------------------------------------------------------------------------
8686

87+
// RestoreOptions contains the optional parameters for the Client.Restore method.
88+
type RestoreOptions struct {
89+
// placeholder for future options
90+
}
91+
92+
// ---------------------------------------------------------------------------------------------------------------------
93+
8794
// GetPropertiesOptions contains the optional parameters for the ContainerClient.GetProperties method.
8895
type GetPropertiesOptions struct {
8996
LeaseAccessConditions *LeaseAccessConditions

sdk/storage/azblob/container/responses.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ type CreateResponse = generated.ContainerClientCreateResponse
1616
// DeleteResponse contains the response from method Client.Delete.
1717
type DeleteResponse = generated.ContainerClientDeleteResponse
1818

19+
// RestoreResponse contains the response from method Client.Restore.
20+
type RestoreResponse = generated.ContainerClientRestoreResponse
21+
1922
// GetPropertiesResponse contains the response from method Client.GetProperties.
2023
type GetPropertiesResponse = generated.ContainerClientGetPropertiesResponse
2124

sdk/storage/azblob/service/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ func (s *Client) DeleteContainer(ctx context.Context, containerName string, opti
123123
return containerDeleteResp, err
124124
}
125125

126+
// RestoreContainer restores soft-deleted container
127+
// Operation will only be successful if used within the specified number of days set in the delete retention policy
128+
func (s *Client) RestoreContainer(ctx context.Context, deletedContainerName string, deletedContainerVersion string, options *RestoreContainerOptions) (RestoreContainerResponse, error) {
129+
containerClient := s.NewContainerClient(deletedContainerName)
130+
containerRestoreResp, err := containerClient.Restore(ctx, deletedContainerVersion, options)
131+
return containerRestoreResp, err
132+
}
133+
126134
// GetAccountInfo provides account level information
127135
func (s *Client) GetAccountInfo(ctx context.Context, o *GetAccountInfoOptions) (GetAccountInfoResponse, error) {
128136
getAccountInfoOptions := o.format()

sdk/storage/azblob/service/client_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,3 +599,64 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() {
599599
//_, err = containerClient2.Create(ctx, nil)
600600
//_require.Nil(err)
601601
}
602+
603+
// make sure that container soft delete is enabled
604+
// TODO: convert this test to recorded
605+
func (s *ServiceUnrecordedTestsSuite) TestContainerRestore() {
606+
_require := require.New(s.T())
607+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
608+
_require.NoError(err)
609+
610+
testName := s.T().Name()
611+
containerName := testcommon.GenerateContainerName(testName)
612+
613+
_, err = svcClient.CreateContainer(context.Background(), containerName, nil)
614+
_require.Nil(err)
615+
616+
_, err = svcClient.DeleteContainer(context.Background(), containerName, nil)
617+
_require.Nil(err)
618+
619+
prefix := testcommon.ContainerPrefix
620+
listOptions := service.ListContainersOptions{Prefix: &prefix, Include: service.ListContainersInclude{Metadata: true, Deleted: true}}
621+
pager := svcClient.NewListContainersPager(&listOptions)
622+
623+
contRestored := false
624+
for pager.More() {
625+
resp, err := pager.NextPage(context.Background())
626+
_require.Nil(err)
627+
for _, cont := range resp.ContainerItems {
628+
_require.NotNil(cont.Name)
629+
630+
if *cont.Deleted && *cont.Name == containerName {
631+
contRestored = true
632+
_, err = svcClient.RestoreContainer(context.Background(), containerName, *cont.Version, nil)
633+
_require.Nil(err)
634+
break
635+
}
636+
}
637+
if contRestored {
638+
break
639+
}
640+
}
641+
642+
_require.Equal(contRestored, true)
643+
644+
_, err = svcClient.DeleteContainer(context.Background(), containerName, nil)
645+
_require.Nil(err)
646+
}
647+
648+
// TODO: convert this test to recorded
649+
func (s *ServiceUnrecordedTestsSuite) TestContainerRestoreFailures() {
650+
_require := require.New(s.T())
651+
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
652+
_require.NoError(err)
653+
654+
testName := s.T().Name()
655+
containerName := testcommon.GenerateContainerName(testName)
656+
657+
_, err = svcClient.RestoreContainer(context.Background(), containerName, "", nil)
658+
testcommon.ValidateBlobErrorCode(_require, err, bloberror.MissingRequiredHeader)
659+
660+
_, err = svcClient.RestoreContainer(context.Background(), "", "", &service.RestoreContainerOptions{})
661+
testcommon.ValidateBlobErrorCode(_require, err, bloberror.MissingRequiredHeader)
662+
}

sdk/storage/azblob/service/examples_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,40 @@ func Example_service_Client_DeleteContainer() {
129129
handleError(err)
130130
}
131131

132+
func Example_service_Client_RestoreContainer() {
133+
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME")
134+
if !ok {
135+
panic("AZURE_STORAGE_ACCOUNT_NAME could not be found")
136+
}
137+
serviceURL := fmt.Sprintf("https://%s.blob.core.windows.net/", accountName)
138+
139+
cred, err := azidentity.NewDefaultAzureCredential(nil)
140+
handleError(err)
141+
serviceClient, err := service.NewClient(serviceURL, cred, nil)
142+
handleError(err)
143+
144+
listOptions := service.ListContainersOptions{
145+
Include: service.ListContainersInclude{
146+
Metadata: true, // Include Metadata
147+
Deleted: true, // Include deleted containers in the result as well
148+
},
149+
}
150+
pager := serviceClient.NewListContainersPager(&listOptions)
151+
152+
for pager.More() {
153+
resp, err := pager.NextPage(context.TODO())
154+
if err != nil {
155+
log.Fatal(err)
156+
}
157+
for _, cont := range resp.ContainerItems {
158+
if *cont.Deleted {
159+
_, err = serviceClient.RestoreContainer(context.TODO(), *cont.Name, *cont.Version, nil)
160+
handleError(err)
161+
}
162+
}
163+
}
164+
}
165+
132166
func Example_service_Client_ListContainers() {
133167
accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME")
134168
if !ok {

sdk/storage/azblob/service/models.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ type CreateContainerOptions = container.CreateOptions
3636
// DeleteContainerOptions contains the optional parameters for the container.Client.Delete method.
3737
type DeleteContainerOptions = container.DeleteOptions
3838

39+
// RestoreContainerOptions contains the optional parameters for the container.Client.Restore method.
40+
type RestoreContainerOptions = container.RestoreOptions
41+
3942
// CorsRule - CORS is an HTTP feature that enables a web application running under one domain to access resources in another
4043
// domain. Web browsers implement a security restriction known as same-origin policy that
4144
// prevents a web page from calling APIs in a different domain; CORS provides a secure way to allow one domain (the origin

sdk/storage/azblob/service/responses.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ type CreateContainerResponse = generated.ContainerClientCreateResponse
1616
// DeleteContainerResponse contains the response from method container.Client.Delete
1717
type DeleteContainerResponse = generated.ContainerClientDeleteResponse
1818

19+
// RestoreContainerResponse contains the response from method container.Client.Restore
20+
type RestoreContainerResponse = generated.ContainerClientRestoreResponse
21+
1922
// GetAccountInfoResponse contains the response from method Client.GetAccountInfo.
2023
type GetAccountInfoResponse = generated.ServiceClientGetAccountInfoResponse
2124

0 commit comments

Comments
 (0)