Skip to content

samples(storage): add samples and test cases for storage to support bucket restore feature #2963

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

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
43 changes: 43 additions & 0 deletions storage/api/Storage.Samples.Tests/GetSoftDeleteBucketTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Cloud.Storage.V1;
using System.Threading.Tasks;
using Xunit;

[Collection(nameof(StorageFixture))]
public class GetSoftDeleteBucketTest
{
private readonly StorageFixture _fixture;

public GetSoftDeleteBucketTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public async Task GetSoftDeleteBucket()
{
GetSoftDeletedBucketSample getSoftDeletedBucketSample = new GetSoftDeletedBucketSample();
var bucketName = _fixture.GenerateBucketName();
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);

var softDeleted = getSoftDeletedBucketSample.GetSoftDeletedBucket(softDeleteBucket.Name, softDeleteBucket.Generation);
Assert.Equal(softDeleteBucket.Name, softDeleted.Name);
Assert.Equal(softDeleteBucket.Generation, softDeleted.Generation);
Assert.NotNull(softDeleted.SoftDeleteTimeDateTimeOffset);
Assert.NotNull(softDeleted.HardDeleteTimeDateTimeOffset);
}
}
51 changes: 51 additions & 0 deletions storage/api/Storage.Samples.Tests/ListSoftDeleteBucketsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Apis.Storage.v1.Data;
using System.Threading.Tasks;
using Xunit;

[Collection(nameof(StorageFixture))]
public class ListSoftDeleteBucketsTest
{
private readonly StorageFixture _fixture;

public ListSoftDeleteBucketsTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public async Task ListSoftDeleteBuckets()
{
ListSoftDeletedBucketsSample listSoftDeletedBucketsSample = new ListSoftDeletedBucketsSample();
var bucketName = _fixture.GenerateBucketName();
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);

var actualBuckets = listSoftDeletedBucketsSample.ListSoftDeletedBuckets(_fixture.ProjectId);
// Check the list contains the bucket we just soft-deleted.
Assert.Contains(actualBuckets, bucket => bucket.Name == softDeleteBucket.Name && bucket.Generation == softDeleteBucket.Generation);
// Check all the buckets in the list are soft-deleted buckets.
Assert.All(actualBuckets, AssertSoftDeletedBucket);
}

// Validates that the given bucket is soft-deleted.
private void AssertSoftDeletedBucket(Bucket b)
{
Assert.NotNull(b.Generation);
Assert.NotNull(b.HardDeleteTimeDateTimeOffset);
Assert.NotNull(b.SoftDeleteTimeDateTimeOffset);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Cloud.Storage.V1;
using System.Threading.Tasks;
using Xunit;

[Collection(nameof(StorageFixture))]
public class RestoreSoftDeleteBucketTest
{
private readonly StorageFixture _fixture;

public RestoreSoftDeleteBucketTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public async Task RestoreSoftDeleteBucket()
{
RestoreSoftDeletedBucketSample restoreSoftDeletedBucketSample = new RestoreSoftDeletedBucketSample();
var bucketName = _fixture.GenerateBucketName();
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);

var restoredBucket = restoreSoftDeletedBucketSample.RestoreSoftDeletedBucket(softDeleteBucket.Name, softDeleteBucket.Generation.Value);
Assert.Equal(softDeleteBucket.Name, restoredBucket.Name);
Assert.Equal(softDeleteBucket.Generation, restoredBucket.Generation);
}
}
24 changes: 23 additions & 1 deletion storage/api/Storage.Samples.Tests/StorageFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google Inc.
// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -47,6 +47,7 @@ public class StorageFixture : IDisposable, ICollectionFixture<StorageFixture>
public string KmsKeyLocation { get; } = "us-west1";
public string ServiceAccountEmail { get; } = "gcs-iam-acl-test@dotnet-docs-samples-tests.iam.gserviceaccount.com";
public List<TopicName> TempTopicNames { get; } = new List<TopicName>();
public StorageClient Client { get; }

public RetryRobot HmacChangesPropagated { get; } = new RetryRobot
{
Expand All @@ -64,6 +65,7 @@ public StorageFixture()
{
throw new Exception("You need to set the Environment variable 'GOOGLE_PROJECT_ID' with your Google Cloud Project's project id.");
}
Client = StorageClient.Create();
// create simple bucket
CreateBucket(BucketNameGeneric);

Expand Down Expand Up @@ -217,6 +219,26 @@ public void CreateBucket(string bucketName, string location = null, AutoclassDat
TempBucketNames.Add(bucketName);
}

internal Bucket CreateBucket(string name, bool multiVersion, bool softDelete = false, bool registerForDeletion = true)
{
var bucket = Client.CreateBucket(ProjectId,
new Bucket
{
Name = name,
Versioning = new Bucket.VersioningData { Enabled = multiVersion },
// The minimum allowed for soft delete is 7 days.
SoftDeletePolicy = softDelete ? new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = (int) TimeSpan.FromDays(7).TotalSeconds } : null,
});
SleepAfterBucketCreateUpdateDelete();
if (registerForDeletion)
{
TempBucketNames.Add(name);
}
return bucket;
}

internal string GenerateBucketName() => Guid.NewGuid().ToString();

/// <summary>
/// Bucket creation/update/deletion is rate-limited. To avoid making the tests flaky, we sleep after each operation.
/// </summary>
Expand Down
45 changes: 45 additions & 0 deletions storage/api/Storage.Samples/GetSoftDeletedBucket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2025 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [START storage_get_soft_deleted_bucket]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;

public class GetSoftDeletedBucketSample
{
/// <summary>
/// Get a soft deleted bucket.
/// </summary>
/// <param name="bucketName">The name of the bucket.</param>
/// <param name="generation">The generation of the bucket.</param>
public Bucket GetSoftDeletedBucket(
string bucketName = "your-unique-bucket-name",
long? generation = 123456789)
{
var client = StorageClient.Create();
var bucket = client.GetBucket(bucketName, new GetBucketOptions
{
SoftDeleted = true,
Generation = generation
});
Console.WriteLine($"Bucket:\t{bucket.Name}");
Console.WriteLine($"Bucket Generation:\t{bucket.Generation}");
Console.WriteLine($"Bucket SoftDelete Time:\t{bucket.SoftDeleteTimeDateTimeOffset}");
Console.WriteLine($"Bucket HardDelete Time:\t{bucket.HardDeleteTimeDateTimeOffset}");
return bucket;
}
}
// [END storage_get_soft_deleted_bucket]
40 changes: 40 additions & 0 deletions storage/api/Storage.Samples/ListSoftDeletedBuckets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2025 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [START storage_list_soft_deleted_buckets]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;
using System.Collections.Generic;

public class ListSoftDeletedBucketsSample
{
/// <summary>
/// List soft deleted buckets.
/// </summary>
/// <param name="projectId">The ID of the project to list soft deleted buckets.</param>
public IEnumerable<Bucket> ListSoftDeletedBuckets(string projectId = "your-project-id")
{
var storage = StorageClient.Create();
var buckets = storage.ListBuckets(projectId, new ListBucketsOptions { SoftDeletedOnly = true });
Console.WriteLine("Soft Deleted Buckets are as follows:");
foreach (var bucket in buckets)
{
Console.WriteLine(bucket.Name);
}
return buckets;
}
}
// [END storage_list_soft_deleted_buckets]
38 changes: 38 additions & 0 deletions storage/api/Storage.Samples/RestoreSoftDeletedBucket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License").
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [START storage_restore_bucket]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;
public class RestoreSoftDeletedBucketSample
{
/// <summary>
/// Restores a soft deleted bucket.
/// </summary>
/// <param name="bucketName">The name of the bucket to restore.</param>
/// <param name="generation">The generation of the bucket.</param>
public Bucket RestoreSoftDeletedBucket(
string bucketName = "your-unique-bucket-name",
long generation = 123456789)
{
var client = StorageClient.Create();
var restored = client.RestoreBucket(bucketName, generation);
Console.WriteLine($"Bucket Name:\t {restored.Name}");
Console.WriteLine($"Bucket Generation:\t {restored.Generation}");
return restored;
}
}
// [END storage_restore_bucket]
2 changes: 1 addition & 1 deletion storage/api/Storage.Samples/Storage.Samples.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
Expand Down