From 3c20ea765b53d8559f49e35a92350719a08f9566 Mon Sep 17 00:00:00 2001 From: Archana Kumari Date: Tue, 22 Apr 2025 07:19:09 +0000 Subject: [PATCH 1/4] chore(secretmanager): Add global samples for delayed destroy --- .../create_secret_with_delayed_destroy.py | 79 +++++++++++++++++++ .../disable_secret_with_delayed_destroy.py | 61 ++++++++++++++ secretmanager/snippets/snippets_test.py | 65 +++++++++++++++ .../update_secret_with_delayed_destroy.py | 66 ++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 secretmanager/snippets/create_secret_with_delayed_destroy.py create mode 100644 secretmanager/snippets/disable_secret_with_delayed_destroy.py create mode 100644 secretmanager/snippets/update_secret_with_delayed_destroy.py diff --git a/secretmanager/snippets/create_secret_with_delayed_destroy.py b/secretmanager/snippets/create_secret_with_delayed_destroy.py new file mode 100644 index 00000000000..f807b7ef2d0 --- /dev/null +++ b/secretmanager/snippets/create_secret_with_delayed_destroy.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# 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 +# +# 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 +""" +command line application and sample code for creating a new secret with +delayed_destroy. +""" + +import argparse + +from google.cloud import secretmanager + +# [START secretmanager_create_secret_with_delayed_destroy] + + +def create_secret_with_delayed_destroy( + project_id: str, + secret_id: str, + version_destroy_ttl: int, +) -> secretmanager.Secret: + """ + Create a new secret with the given name and version destroy ttl. A + secret is a logical wrapper around a collection of secret versions. + Secret versions hold the actual secret material. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager + + # Import the Duration protobuf library. + from google.protobuf.duration_pb2 import Duration + + # Create the Secret Manager client. + client = secretmanager.SecretManagerServiceClient() + + # Build the resource name of the parent project. + parent = f"projects/{project_id}" + + # Create the secret. + response = client.create_secret( + request={ + "parent": parent, + "secret_id": secret_id, + "secret": { + "replication": {"automatic": {}}, + "version_destroy_ttl": Duration(seconds=version_destroy_ttl), + }, + } + ) + + # Print the new secret name. + print(f"Created secret: {response.name}") + + return response + + +# [END secretmanager_create_secret_with_delayed_destroy] + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument("secret_id", help="id of the secret to create") + parser.add_argument("version_destroy_ttl", help="version_destroy_ttl you want to add") + args = parser.parse_args() + + create_secret_with_delayed_destroy(args.project_id, args.secret_id, args.version_destroy_ttl) diff --git a/secretmanager/snippets/disable_secret_with_delayed_destroy.py b/secretmanager/snippets/disable_secret_with_delayed_destroy.py new file mode 100644 index 00000000000..18625c28ad3 --- /dev/null +++ b/secretmanager/snippets/disable_secret_with_delayed_destroy.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +# 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 +# +# 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 + +import argparse + +from google.cloud import secretmanager + +# [START secretmanager_disable_secret_with_delayed_destroy] + + +def disable_secret_with_delayed_destroy( + project_id: str, secret_id: str +) -> secretmanager.Secret: + """ + Disable the version destroy ttl on the given secret version. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager + + # Create the Secret Manager client. + client = secretmanager.SecretManagerServiceClient() + + # Build the resource name of the secret. + name = client.secret_path(project_id, secret_id) + + # Disable delayed destroy of the secret. + secret = {"name": name} + update_mask = {"paths": ["version_destroy_ttl"]} + response = client.update_secret(request={"secret": secret, "update_mask": update_mask}) + + # Print the new secret name. + print(f"Disabled delayed destroy on secret: {response.name}") + # [END secretmanager_disable_secret_with_delayed_destroy] + + return response + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument("secret_id", help="id of the secret from which to act") + args = parser.parse_args() + + disable_secret_with_delayed_destroy( + args.project_id, args.secret_id + ) diff --git a/secretmanager/snippets/snippets_test.py b/secretmanager/snippets/snippets_test.py index 790942a3e6f..fc6b94ecad0 100644 --- a/secretmanager/snippets/snippets_test.py +++ b/secretmanager/snippets/snippets_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and import base64 +from datetime import timedelta import os import time from typing import Iterator, Optional, Tuple, Union @@ -19,6 +20,8 @@ from google.api_core import exceptions, retry from google.cloud import secretmanager +from google.protobuf.duration_pb2 import Duration + import pytest from access_secret_version import access_secret_version @@ -26,6 +29,7 @@ from consume_event_notification import consume_event_notification from create_secret import create_secret from create_secret_with_annotations import create_secret_with_annotations +from create_secret_with_delayed_destroy import create_secret_with_delayed_destroy from create_secret_with_labels import create_secret_with_labels from create_secret_with_user_managed_replication import create_ummr_secret from create_update_secret_label import create_update_secret_label @@ -36,6 +40,7 @@ from destroy_secret_version_with_etag import destroy_secret_version_with_etag from disable_secret_version import disable_secret_version from disable_secret_version_with_etag import disable_secret_version_with_etag +from disable_secret_with_delayed_destroy import disable_secret_with_delayed_destroy from edit_secret_annotations import edit_secret_annotations from enable_secret_version import enable_secret_version from enable_secret_version_with_etag import enable_secret_version_with_etag @@ -50,6 +55,7 @@ from quickstart import quickstart from update_secret import update_secret from update_secret_with_alias import update_secret_with_alias +from update_secret_with_delayed_destroy import update_secret_with_delayed_destroy from update_secret_with_etag import update_secret_with_etag from view_secret_annotations import view_secret_annotations from view_secret_labels import view_secret_labels @@ -95,6 +101,11 @@ def annotation_value() -> str: return "annotationvalue" +@pytest.fixture() +def version_destroy_ttl() -> str: + return 604800 # 7 days in seconds + + @retry.Retry() def retry_client_create_secret( client: secretmanager.SecretManagerServiceClient, @@ -180,6 +191,33 @@ def secret( yield project_id, secret_id, secret.etag +@pytest.fixture() +def secret_with_delayed_destroy( + client: secretmanager.SecretManagerServiceClient, + project_id: str, + secret_id: str, + version_destroy_ttl: int, + ttl: Optional[str], +) -> Iterator[Tuple[str, str]]: + print(f"creating secret {secret_id}") + + parent = f"projects/{project_id}" + time.sleep(5) + retry_client_create_secret( + client, + request={ + "parent": parent, + "secret_id": secret_id, + "secret": { + "replication": {"automatic": {}}, + "version_destroy_ttl": Duration(seconds=version_destroy_ttl), + }, + }, + ) + + yield project_id, secret_id + + @pytest.fixture() def secret_version( client: secretmanager.SecretManagerServiceClient, secret: Tuple[str, str, str] @@ -288,6 +326,17 @@ def test_create_secret_with_annotations( assert secret_id in secret.name +def test_create_secret_with_delayed_destroy( + client: secretmanager.SecretManagerServiceClient, + project_id: str, + secret_id: str, + version_destroy_ttl: int, +) -> None: + secret = create_secret_with_delayed_destroy(project_id, secret_id, version_destroy_ttl) + assert secret_id in secret.name + assert timedelta(seconds=version_destroy_ttl) == secret.version_destroy_ttl + + def test_delete_secret( client: secretmanager.SecretManagerServiceClient, secret: Tuple[str, str, str] ) -> None: @@ -341,6 +390,15 @@ def test_destroy_secret_version_with_etag( assert version.destroy_time +def test_disable_secret_with_delayed_destroy( + client: secretmanager.SecretManagerServiceClient, + secret_with_delayed_destroy: Tuple[str, str], +) -> None: + project_id, secret_id = secret_with_delayed_destroy + updated_secret = disable_secret_with_delayed_destroy(project_id, secret_id) + assert updated_secret.version_destroy_ttl == timedelta(0) + + def test_enable_disable_secret_version( client: secretmanager.SecretManagerServiceClient, secret_version: Tuple[str, str, str, str], @@ -532,3 +590,10 @@ def test_update_secret_with_alias(secret_version: Tuple[str, str, str, str]) -> project_id, secret_id, version_id, _ = secret_version secret = update_secret_with_alias(project_id, secret_id) assert secret.version_aliases["test"] == 1 + + +def test_update_secret_with_delayed_destroy(secret_with_delayed_destroy: Tuple[str, str], version_destroy_ttl: str) -> None: + project_id, secret_id = secret_with_delayed_destroy + updated_version_destroy_ttl_value = 118400 + updated_secret = update_secret_with_delayed_destroy(project_id, secret_id, updated_version_destroy_ttl_value) + assert updated_secret.version_destroy_ttl == timedelta(seconds=version_destroy_ttl) diff --git a/secretmanager/snippets/update_secret_with_delayed_destroy.py b/secretmanager/snippets/update_secret_with_delayed_destroy.py new file mode 100644 index 00000000000..03e792d0e8a --- /dev/null +++ b/secretmanager/snippets/update_secret_with_delayed_destroy.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# 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 +# +# 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 + +import argparse + +from google.cloud import secretmanager + +# [START secretmanager_update_secret_with_delayed_destroy] + + +def update_secret_with_delayed_destroy( + project_id: str, secret_id: str, new_version_destroy_ttl: int +) -> secretmanager.UpdateSecretRequest: + """ + Update the version destroy ttl value on an existing secret. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager + + # from datetime import timedelta + from google.protobuf.duration_pb2 import Duration + + # Create the Secret Manager client. + client = secretmanager.SecretManagerServiceClient() + + # Build the resource name of the secret. + name = client.secret_path(project_id, secret_id) + + # Update the version_destroy_ttl. + secret = {"name": name, "version_destroy_ttl": Duration(seconds=new_version_destroy_ttl)} + update_mask = {"paths": ["version_destroy_ttl"]} + response = client.update_secret( + request={"secret": secret, "update_mask": update_mask} + ) + + # Print the new secret name. + print(f"Updated secret: {response.name}") + + return response + + # [END secretmanager_update_secret_with_delayed_destroy] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument("secret-id", help="id of the secret to act on") + parser.add_argument("version_destroy_ttl", "new version destroy ttl to be added") + args = parser.parse_args() + + update_secret_with_delayed_destroy(args.project_id, args.secret_id, args.version_destroy_ttl) From 07e6e3686146aa6074857ca237e6d8e1e9ac2376 Mon Sep 17 00:00:00 2001 From: Archana Kumari Date: Tue, 22 Apr 2025 08:11:53 +0000 Subject: [PATCH 2/4] fix: update secretmanager global tests for delayed destroy --- secretmanager/snippets/snippets_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/secretmanager/snippets/snippets_test.py b/secretmanager/snippets/snippets_test.py index fc6b94ecad0..8505aab7aba 100644 --- a/secretmanager/snippets/snippets_test.py +++ b/secretmanager/snippets/snippets_test.py @@ -170,7 +170,7 @@ def secret( annotation_value: str, ttl: Optional[str], ) -> Iterator[Tuple[str, str, str, str]]: - print(f"creating secret {secret_id}") + print(f"creating secret with given secret id.") parent = f"projects/{project_id}" time.sleep(5) @@ -596,4 +596,4 @@ def test_update_secret_with_delayed_destroy(secret_with_delayed_destroy: Tuple[s project_id, secret_id = secret_with_delayed_destroy updated_version_destroy_ttl_value = 118400 updated_secret = update_secret_with_delayed_destroy(project_id, secret_id, updated_version_destroy_ttl_value) - assert updated_secret.version_destroy_ttl == timedelta(seconds=version_destroy_ttl) + assert updated_secret.version_destroy_ttl == timedelta(seconds=updated_version_destroy_ttl_value) From 1ec264a7c721bfae6425ca560949904d1b0fdf59 Mon Sep 17 00:00:00 2001 From: Archana Kumari Date: Tue, 22 Apr 2025 09:05:36 +0000 Subject: [PATCH 3/4] Refactor secretmanager tests for global samples --- .../snippets/create_secret_with_delayed_destroy.py | 11 ++++------- .../snippets/disable_secret_with_delayed_destroy.py | 5 ++--- secretmanager/snippets/snippets_test.py | 8 +++----- .../snippets/update_secret_with_delayed_destroy.py | 10 +++------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/secretmanager/snippets/create_secret_with_delayed_destroy.py b/secretmanager/snippets/create_secret_with_delayed_destroy.py index f807b7ef2d0..01b47223bb2 100644 --- a/secretmanager/snippets/create_secret_with_delayed_destroy.py +++ b/secretmanager/snippets/create_secret_with_delayed_destroy.py @@ -17,11 +17,14 @@ delayed_destroy. """ +# [START secretmanager_create_secret_with_delayed_destroy] import argparse +# Import the Secret Manager client library. from google.cloud import secretmanager -# [START secretmanager_create_secret_with_delayed_destroy] +# from datetime import timedelta +from google.protobuf.duration_pb2 import Duration def create_secret_with_delayed_destroy( @@ -35,12 +38,6 @@ def create_secret_with_delayed_destroy( Secret versions hold the actual secret material. """ - # Import the Secret Manager client library. - from google.cloud import secretmanager - - # Import the Duration protobuf library. - from google.protobuf.duration_pb2 import Duration - # Create the Secret Manager client. client = secretmanager.SecretManagerServiceClient() diff --git a/secretmanager/snippets/disable_secret_with_delayed_destroy.py b/secretmanager/snippets/disable_secret_with_delayed_destroy.py index 18625c28ad3..2d3a75fca6e 100644 --- a/secretmanager/snippets/disable_secret_with_delayed_destroy.py +++ b/secretmanager/snippets/disable_secret_with_delayed_destroy.py @@ -17,9 +17,8 @@ from google.cloud import secretmanager -# [START secretmanager_disable_secret_with_delayed_destroy] - +# [START secretmanager_disable_secret_with_delayed_destroy] def disable_secret_with_delayed_destroy( project_id: str, secret_id: str ) -> secretmanager.Secret: @@ -36,7 +35,7 @@ def disable_secret_with_delayed_destroy( # Build the resource name of the secret. name = client.secret_path(project_id, secret_id) - # Disable delayed destroy of the secret. + # Delayed destroy of the secret version. secret = {"name": name} update_mask = {"paths": ["version_destroy_ttl"]} response = client.update_secret(request={"secret": secret, "update_mask": update_mask}) diff --git a/secretmanager/snippets/snippets_test.py b/secretmanager/snippets/snippets_test.py index 8505aab7aba..93504d217f5 100644 --- a/secretmanager/snippets/snippets_test.py +++ b/secretmanager/snippets/snippets_test.py @@ -170,7 +170,7 @@ def secret( annotation_value: str, ttl: Optional[str], ) -> Iterator[Tuple[str, str, str, str]]: - print(f"creating secret with given secret id.") + print(f"creating secret {secret_id}") parent = f"projects/{project_id}" time.sleep(5) @@ -199,7 +199,7 @@ def secret_with_delayed_destroy( version_destroy_ttl: int, ttl: Optional[str], ) -> Iterator[Tuple[str, str]]: - print(f"creating secret {secret_id}") + print("creating secret with given secret id.") parent = f"projects/{project_id}" time.sleep(5) @@ -328,9 +328,7 @@ def test_create_secret_with_annotations( def test_create_secret_with_delayed_destroy( client: secretmanager.SecretManagerServiceClient, - project_id: str, - secret_id: str, - version_destroy_ttl: int, + project_id: str, secret_id: str, version_destroy_ttl: int ) -> None: secret = create_secret_with_delayed_destroy(project_id, secret_id, version_destroy_ttl) assert secret_id in secret.name diff --git a/secretmanager/snippets/update_secret_with_delayed_destroy.py b/secretmanager/snippets/update_secret_with_delayed_destroy.py index 03e792d0e8a..8dcfd40f97a 100644 --- a/secretmanager/snippets/update_secret_with_delayed_destroy.py +++ b/secretmanager/snippets/update_secret_with_delayed_destroy.py @@ -17,9 +17,11 @@ from google.cloud import secretmanager -# [START secretmanager_update_secret_with_delayed_destroy] +# from datetime import timedelta +from google.protobuf.duration_pb2 import Duration +# [START secretmanager_update_secret_with_delayed_destroy] def update_secret_with_delayed_destroy( project_id: str, secret_id: str, new_version_destroy_ttl: int ) -> secretmanager.UpdateSecretRequest: @@ -27,12 +29,6 @@ def update_secret_with_delayed_destroy( Update the version destroy ttl value on an existing secret. """ - # Import the Secret Manager client library. - from google.cloud import secretmanager - - # from datetime import timedelta - from google.protobuf.duration_pb2 import Duration - # Create the Secret Manager client. client = secretmanager.SecretManagerServiceClient() From 3d1a3e0b869cf6dcdfbcaac06fa7ca84650505b2 Mon Sep 17 00:00:00 2001 From: Archana Kumari Date: Wed, 23 Apr 2025 16:17:10 +0000 Subject: [PATCH 4/4] chore(secretmanager): Add regional samples for delayed destory --- ...te_regional_secret_with_delayed_destroy.py | 85 +++++++++++++++++++ ...disable_regional_secret_delayed_destroy.py | 73 ++++++++++++++++ .../regional_samples/snippets_test.py | 72 ++++++++++++++++ ...te_regional_secret_with_delayed_destroy.py | 77 +++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 secretmanager/snippets/regional_samples/create_regional_secret_with_delayed_destroy.py create mode 100644 secretmanager/snippets/regional_samples/disable_regional_secret_delayed_destroy.py create mode 100644 secretmanager/snippets/regional_samples/update_regional_secret_with_delayed_destroy.py diff --git a/secretmanager/snippets/regional_samples/create_regional_secret_with_delayed_destroy.py b/secretmanager/snippets/regional_samples/create_regional_secret_with_delayed_destroy.py new file mode 100644 index 00000000000..c07083e7aad --- /dev/null +++ b/secretmanager/snippets/regional_samples/create_regional_secret_with_delayed_destroy.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +# Copyright 2024 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 +# +# 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 +""" +Command line application and sample code for creating a new secret with +delayed destroy. +""" + +import argparse + +# [START secretmanager_create_regional_secret_with_delayed_destroy] + +# Import the Secret Manager client library. +from google.cloud import secretmanager_v1 +from google.protobuf.duration_pb2 import Duration + + +def create_regional_secret_with_delayed_destroy( + project_id: str, + location_id: str, + secret_id: str, + version_destroy_ttl: int, +) -> secretmanager_v1.Secret: + """ + Create a new secret with the given name and version_destroy_ttl. A secret is a logical wrapper + around a collection of secret versions. Secret versions hold the actual + secret material. + """ + + # Endpoint to call the regional secret manager sever + api_endpoint = f"secretmanager.{location_id}.rep.googleapis.com" + + # Create the Secret Manager client. + client = secretmanager_v1.SecretManagerServiceClient( + client_options={"api_endpoint": api_endpoint}, + ) + + # Build the resource name of the parent project. + parent = f"projects/{project_id}/locations/{location_id}" + + # Create the secret. + response = client.create_secret( + request={ + "parent": parent, + "secret_id": secret_id, + "secret": {"version_destroy_ttl": Duration(seconds=version_destroy_ttl)}, + } + ) + + # Print the new secret name. + print(f"Created secret: {response.name}") + + return response + +# [END secretmanager_create_regional_secret_with_delayed_destroy] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument( + "location_id", help="id of the location where secret is to be created" + ) + parser.add_argument("secret_id", help="id of the secret to create") + parser.add_argument( + "version_destroy_ttl", help="version_destroy_ttl you want to add" + ) + args = parser.parse_args() + + create_regional_secret_with_delayed_destroy( + args.project_id, args.location_id, args.secret_id, args.version_destroy_ttl + ) diff --git a/secretmanager/snippets/regional_samples/disable_regional_secret_delayed_destroy.py b/secretmanager/snippets/regional_samples/disable_regional_secret_delayed_destroy.py new file mode 100644 index 00000000000..b4b4c74a09e --- /dev/null +++ b/secretmanager/snippets/regional_samples/disable_regional_secret_delayed_destroy.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +# Copyright 2024 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 +# +# 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 + +import argparse + +# [START secretmanager_disable_regional_secret_delayed_destroy] + +# Import the Secret Manager client library. +from google.cloud import secretmanager_v1 + + +def disable_regional_secret_delayed_destroy( + project_id: str, location_id: str, secret_id: str +) -> secretmanager_v1.Secret: + """ + Disable delayed destroy on an existing secret with a version destroy ttl. + """ + + # Endpoint to call the regional secret manager sever + api_endpoint = f"secretmanager.{location_id}.rep.googleapis.com" + + # Create the Secret Manager client. + client = secretmanager_v1.SecretManagerServiceClient( + client_options={"api_endpoint": api_endpoint}, + ) + + # Build the resource name. + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}" + + # Disable delayed destroy on secret + secret = {"name": name} + update_mask = {"paths": ["version_destroy_ttl"]} + response = client.update_secret( + request={"secret": secret, "update_mask": update_mask} + ) + + # Print the new secret name. + print(f"Disabled delayed destroy on secret: {response.name}") + + return response + +# [END secretmanager_disable_regional_secret_delayed_destroy] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument( + "location_id", help="id of the location where secret is to be created" + ) + parser.add_argument("secret_id", help="id of the secret to act on") + parser.add_argument( + "version_destroy_ttl", help="version_destroy_ttl you want to add" + ) + args = parser.parse_args() + + disable_regional_secret_delayed_destroy( + args.project_id, args.location_id, args.secret_id, args.version_destroy_ttl + ) diff --git a/secretmanager/snippets/regional_samples/snippets_test.py b/secretmanager/snippets/regional_samples/snippets_test.py index d605be84dfc..a6a8748314c 100644 --- a/secretmanager/snippets/regional_samples/snippets_test.py +++ b/secretmanager/snippets/regional_samples/snippets_test.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +from datetime import timedelta import os import time from typing import Iterator, Tuple, Union @@ -18,18 +19,21 @@ from google.api_core import exceptions, retry from google.cloud import secretmanager_v1 +from google.protobuf.duration_pb2 import Duration import pytest from regional_samples import access_regional_secret_version from regional_samples import add_regional_secret_version from regional_samples import create_regional_secret from regional_samples import create_regional_secret_with_annotations +from regional_samples import create_regional_secret_with_delayed_destroy from regional_samples import create_regional_secret_with_labels from regional_samples import delete_regional_secret from regional_samples import delete_regional_secret_label from regional_samples import delete_regional_secret_with_etag from regional_samples import destroy_regional_secret_version from regional_samples import destroy_regional_secret_version_with_etag +from regional_samples import disable_regional_secret_delayed_destroy from regional_samples import disable_regional_secret_version from regional_samples import disable_regional_secret_version_with_etag from regional_samples import edit_regional_secret_annotations @@ -46,6 +50,7 @@ from regional_samples import list_regional_secrets_with_filter from regional_samples import regional_quickstart from regional_samples import update_regional_secret +from regional_samples import update_regional_secret_with_delayed_destroy from regional_samples import update_regional_secret_with_etag from regional_samples import view_regional_secret_annotations from regional_samples import view_regional_secret_labels @@ -99,6 +104,11 @@ def annotation_value() -> str: return "annotationvalue" +@pytest.fixture() +def version_destroy_ttl() -> int: + return 604800 # 7 days in seconds + + @retry.Retry() def retry_client_create_regional_secret( regional_client: secretmanager_v1.SecretManagerServiceClient, @@ -201,6 +211,33 @@ def regional_secret( yield secret_id, regional_secret.etag +@pytest.fixture() +def regional_secret_with_delayed_destroy( + regional_client: secretmanager_v1.SecretManagerServiceClient, + project_id: str, + location_id: str, + secret_id: str, + version_destroy_ttl: int, +): + print("creating secret with given secret id.") + + parent = f"projects/{project_id}/locations/{location_id}" + time.sleep(5) + retry_client_create_regional_secret( + regional_client, + request={ + "parent": parent, + "secret_id": secret_id, + "secret": { + "version_destroy_ttl": Duration(seconds=version_destroy_ttl), + }, + }, + ) + print("debug") + + yield secret_id + + def test_regional_quickstart(project_id: str, location_id: str, secret_id: str) -> None: regional_quickstart.regional_quickstart(project_id, location_id, secret_id) @@ -259,6 +296,18 @@ def test_create_regional_secret_with_annotations( assert secret_id in secret.name +def test_create_regional_secret_with_delayed_destroy( + regional_client: secretmanager_v1.SecretManagerServiceClient, + project_id: str, + location_id: str, + secret_id: str, + version_destroy_ttl: int, +) -> None: + secret = create_regional_secret_with_delayed_destroy.create_regional_secret_with_delayed_destroy(project_id, location_id, secret_id, version_destroy_ttl) + assert secret_id in secret.name + assert timedelta(seconds=version_destroy_ttl) == secret.version_destroy_ttl + + def test_create_regional_secret_with_label( regional_client: secretmanager_v1.SecretManagerServiceClient, project_id: str, @@ -336,6 +385,17 @@ def test_destroy_regional_secret_version_with_etag( assert version.destroy_time +def test_disable_regional_secret_delayed_destroy( + regional_client: secretmanager_v1.SecretManagerServiceClient, + regional_secret_with_delayed_destroy: str, + project_id: str, + location_id: str, +) -> None: + secret_id = regional_secret_with_delayed_destroy + updated_secret = disable_regional_secret_delayed_destroy.disable_regional_secret_delayed_destroy(project_id, location_id, secret_id) + assert updated_secret.version_destroy_ttl == timedelta(0) + + def test_enable_disable_regional_secret_version( regional_client: secretmanager_v1.SecretManagerServiceClient, regional_secret_version: Tuple[str, str, str], @@ -612,6 +672,18 @@ def test_update_regional_secret( assert updated_regional_secret.labels["secretmanager"] == "rocks" +def test_update_regional_secret_with_delayed_destroy( + regional_secret_with_delayed_destroy: str, + project_id: str, + location_id: str, + version_destroy_ttl: int +) -> None: + secret_id = regional_secret_with_delayed_destroy + updated_version_delayed_destroy = 118400 + updated_secret = update_regional_secret_with_delayed_destroy.update_regional_secret_with_delayed_destroy(project_id, location_id, secret_id, updated_version_delayed_destroy) + assert updated_secret.version_destroy_ttl == timedelta(seconds=updated_version_delayed_destroy) + + def test_view_regional_secret_labels( capsys: pytest.LogCaptureFixture, project_id: str, diff --git a/secretmanager/snippets/regional_samples/update_regional_secret_with_delayed_destroy.py b/secretmanager/snippets/regional_samples/update_regional_secret_with_delayed_destroy.py new file mode 100644 index 00000000000..0cc829d0b40 --- /dev/null +++ b/secretmanager/snippets/regional_samples/update_regional_secret_with_delayed_destroy.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Copyright 2024 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 +# +# 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 + +import argparse + +# [START secretmanager_update_regional_secret_delayed_destroy] + +# Import the Secret Manager client library. +from google.cloud import secretmanager_v1 +from google.protobuf.duration_pb2 import Duration + + +def update_regional_secret_with_delayed_destroy( + project_id: str, location_id: str, secret_id: str, new_version_destroy_ttl: int +) -> secretmanager_v1.UpdateSecretRequest: + """ + Update the version destroy ttl on an existing secret. + """ + + # Endpoint to call the regional secret manager sever + api_endpoint = f"secretmanager.{location_id}.rep.googleapis.com" + + # Create the Secret Manager client. + client = secretmanager_v1.SecretManagerServiceClient( + client_options={"api_endpoint": api_endpoint}, + ) + + # Build the resource name. + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}" + + # Get the secret. + response = client.get_secret(request={"name": name}) + + # Update the secret. + secret = {"name": name, "version_destroy_ttl": Duration(seconds=new_version_destroy_ttl)} + update_mask = {"paths": ["version_destroy_ttl"]} + response = client.update_secret( + request={"secret": secret, "update_mask": update_mask} + ) + + # Print the new secret name. + print(f"Updated secret: {response.name}") + + return response + +# [END secretmanager_update_regional_secret_delayed_destroy] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("project_id", help="id of the GCP project") + parser.add_argument( + "location_id", help="id of the location where secret is to be created" + ) + parser.add_argument("secret_id", help="id of the secret to act on") + parser.add_argument( + "version_destroy_ttl", help="version_destroy_ttl you want to add" + ) + args = parser.parse_args() + + update_regional_secret_with_delayed_destroy( + args.project_id, args.location_id, args.secret_id, args.version_destroy_ttl + )