diff --git a/secretmanager/snippets/regional_samples/access_regional_secret_version.py b/secretmanager/snippets/regional_samples/access_regional_secret_version.py new file mode 100644 index 00000000000..a1f08edce04 --- /dev/null +++ b/secretmanager/snippets/regional_samples/access_regional_secret_version.py @@ -0,0 +1,76 @@ +#!/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 accessing a regional secret version. +""" + +import argparse + +# [START secretmanager_access_regional_secret_version] +from google.cloud import secretmanager_v1 +import google_crc32c + + +def access_regional_secret_version( + project_id: str, location_id: str, secret_id: str, version_id: str +) -> secretmanager_v1.AccessSecretVersionResponse: + """ + Access the payload for the given secret version if one exists. The version + can be a version number as a string (e.g. "5") or an alias (e.g. "latest"). + """ + + # 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 secret version. + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}/versions/{version_id}" + + # Access the secret version. + response = client.access_secret_version(request={"name": name}) + + # Verify payload checksum. + crc32c = google_crc32c.Checksum() + crc32c.update(response.payload.data) + if response.payload.data_crc32c != int(crc32c.hexdigest(), 16): + print("Data corruption detected.") + return response + + # Print the secret payload. + # + # WARNING: Do not print the secret in a production environment - this + # snippet is showing how to access the secret material. + payload = response.payload.data.decode("UTF-8") + print(f"Plaintext: {payload}") + # [END secretmanager_access_regional_secret_version] + + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret to access") + parser.add_argument("version_id", help="version to access") + args = parser.parse_args() + + access_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.version_id) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/add_regional_secret_version.py b/secretmanager/snippets/regional_samples/add_regional_secret_version.py new file mode 100644 index 00000000000..0bc79c786ac --- /dev/null +++ b/secretmanager/snippets/regional_samples/add_regional_secret_version.py @@ -0,0 +1,81 @@ +#!/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 adding a secret version to a +regional secret with the specified payload to an existing regional secret. +""" + +import argparse + +# [START secretmanager_v1_add_regional_secret_version] +from google.cloud import secretmanager_v1 +import google_crc32c # type: ignore + + +def add_regional_secret_version( + project_id: str, location_id: str, secret_id: str, payload: str +) -> secretmanager_v1.SecretVersion: + """ + Add a new secret version to the given secret with the provided payload. + """ + + # 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 secret. + parent = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}" + + # Convert the string payload into a bytes. This step can be omitted if you + # pass in bytes instead of a str for the payload argument. + payload_bytes = payload.encode("UTF-8") + + # Calculate payload checksum. Passing a checksum in add-version request + # is optional. + crc32c = google_crc32c.Checksum() + crc32c.update(payload_bytes) + + # Add the secret version. + response = client.add_secret_version( + request={ + "parent": parent, + "payload": { + "data": payload_bytes, + "data_crc32c": int(crc32c.hexdigest(), 16), + }, + } + ) + + # Print the new secret version name. + print(f"Added secret version: {response.name}") + # [END secretmanager_v1_add_regional_secret_version] + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret in which to add") + parser.add_argument("payload", help="secret material payload") + args = parser.parse_args() + + add_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.payload) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/destroy_regional_secret_version.py b/secretmanager/snippets/regional_samples/destroy_regional_secret_version.py new file mode 100644 index 00000000000..b68cb9db9ae --- /dev/null +++ b/secretmanager/snippets/regional_samples/destroy_regional_secret_version.py @@ -0,0 +1,67 @@ +#!/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 destroying a regional +secret version. +""" + +import argparse + +from google.cloud import secretmanager_v1 + + +# [START secretmanager_v1_destroy_regional_secret_version] +def destroy_regional_secret_version( + project_id: str, location_id: str, secret_id: str, version_id: str +) -> secretmanager_v1.DestroySecretVersionRequest: + """ + Destroy the given secret version, making the payload irrecoverable. Other + secrets versions are unaffected. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager_v1 + + # 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 secret version + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}/versions/{version_id}" + + # Destroy the secret version. + response = client.destroy_secret_version(request={"name": name}) + + print(f"Destroyed secret version: {response.name}") + # [END secretmanager_v1_destroy_regional_secret_version] + + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret from which to act") + parser.add_argument("version_id", help="id of the version to destroy") + args = parser.parse_args() + + destroy_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.version_id) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/disable_regional_secret_version.py b/secretmanager/snippets/regional_samples/disable_regional_secret_version.py new file mode 100644 index 00000000000..d3bb83f3b72 --- /dev/null +++ b/secretmanager/snippets/regional_samples/disable_regional_secret_version.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# Copyright 2019 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 disabling a regional +secret version. +""" + +import argparse + +from google.cloud import secretmanager_v1 + + +# [START secretmanager_v1_disable_regional_secret_version] +def disable_regional_secret_version( + project_id: str, location_id: str, secret_id: str, version_id: str +) -> secretmanager_v1.DisableSecretVersionRequest: + """ + Disable the given secret version. Future requests will throw an error until + the secret version is enabled. Other secrets versions are unaffected. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager_v1 + + # 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 secret version + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}/versions/{version_id}" + + # Disable the secret version. + response = client.disable_secret_version(request={"name": name}) + + print(f"Disabled secret version: {response.name}") + # [END secretmanager_v1_disable_regional_secret_version] + + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret from which to act") + parser.add_argument("version_id", help="id of the version to disable") + args = parser.parse_args() + + disable_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.version_id) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/enable_regional_secret_version.py b/secretmanager/snippets/regional_samples/enable_regional_secret_version.py new file mode 100644 index 00000000000..84979a1e4e1 --- /dev/null +++ b/secretmanager/snippets/regional_samples/enable_regional_secret_version.py @@ -0,0 +1,67 @@ +#!/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 enabling a regional +secret version. +""" + +import argparse + +from google.cloud import secretmanager_v1 + + +# [START secretmanager_enable_regional_secret_version] +def enable_regional_secret_version( + project_id: str, location_id: str, secret_id: str, version_id: str +) -> secretmanager_v1.EnableSecretVersionRequest: + """ + Enable the given secret version, enabling it to be accessed after + previously being disabled. Other secrets versions are unaffected. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager_v1 + + # 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 secret version + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}/versions/{version_id}" + + # Disable the secret version. + response = client.enable_secret_version(request={"name": name}) + + print(f"Enabled secret version: {response.name}") + # [END secretmanager_enable_regional_secret_version] + + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret from which to act") + parser.add_argument("version_id", help="id of the version to enable") + args = parser.parse_args() + + enable_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.version_id) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/get_regional_secret_version.py b/secretmanager/snippets/regional_samples/get_regional_secret_version.py new file mode 100644 index 00000000000..966252911ae --- /dev/null +++ b/secretmanager/snippets/regional_samples/get_regional_secret_version.py @@ -0,0 +1,69 @@ +#!/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 getting metadata about a regional +secret version, but not the secret payload. +""" + +import argparse + +from google.cloud import secretmanager_v1 + + +# [START secretmanager_v1_get_regional_secret_version] +def get_regional_secret_version( + project_id: str, location_id: str, secret_id: str, version_id: str +) -> secretmanager_v1.GetSecretVersionRequest: + """ + Get information about the given secret version. It does not include the + payload data. + """ + + # Import the Secret Manager client library. + from google.cloud import secretmanager_v1 + + # 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 secret version. + name = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}/versions/{version_id}" + + # Get the secret version. + response = client.get_secret_version(request={"name": name}) + + # Print information about the secret version. + state = response.state.name + print(f"Got secret version {response.name} with state {state}") + # [END secretmanager_v1_get_regional_secret_version] + + 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("location_id", help="id of location where secret is stored") + parser.add_argument("secret_id", help="id of the secret from which to act") + parser.add_argument("version_id", help="id of the version to get") + args = parser.parse_args() + + get_regional_secret_version(args.project_id, args.location_id, args.secret_id, args.version_id) \ No newline at end of file diff --git a/secretmanager/snippets/regional_samples/snippets_test.py b/secretmanager/snippets/regional_samples/snippets_test.py index 9c0b0878d94..1e3264a529a 100644 --- a/secretmanager/snippets/regional_samples/snippets_test.py +++ b/secretmanager/snippets/regional_samples/snippets_test.py @@ -21,9 +21,23 @@ from google.cloud import secretmanager, secretmanager_v1 import pytest +from regional_samples.access_regional_secret_version import ( + access_regional_secret_version, +) +from regional_samples.add_regional_secret_version import add_regional_secret_version from regional_samples.create_regional_secret import create_regional_secret from regional_samples.delete_regional_secret import delete_regional_secret +from regional_samples.destroy_regional_secret_version import ( + destroy_regional_secret_version, +) +from regional_samples.disable_regional_secret_version import ( + disable_regional_secret_version, +) +from regional_samples.enable_regional_secret_version import ( + enable_regional_secret_version, +) from regional_samples.get_regional_secret import get_regional_secret +from regional_samples.get_regional_secret_version import get_regional_secret_version from regional_samples.regional_quickstart import regional_quickstart from regional_samples.update_regional_secret import update_regional_secret @@ -96,6 +110,28 @@ def secret_id( # Secret was already deleted, probably in the test print(f"Secret {secret_id} was not found.") +@pytest.fixture() +def regional_secret_version( + regional_client: secretmanager_v1.SecretManagerServiceClient, + regional_secret: Tuple[str, str, str, str], +) -> Iterator[Tuple[str, str, str, str, str]]: + project_id, location_id, secret_id, _ = regional_secret + + print(f"adding secret version to {secret_id}") + parent = f"projects/{project_id}/locations/{location_id}/secrets/{secret_id}" + payload = b"hello world!" + time.sleep(5) + version = regional_client.add_secret_version( + request={"parent": parent, "payload": {"data": payload}} + ) + + yield project_id, location_id, secret_id, version.name.rsplit("/", 1)[ + -1 + ], version.etag + + +another_regional_secret_version = regional_secret_version + @pytest.fixture() def regional_secret( regional_client: secretmanager_v1.SecretManagerServiceClient, @@ -122,6 +158,23 @@ def regional_secret( def test_regional_quickstart(project_id: str, location_id: str, secret_id: str) -> None: regional_quickstart(project_id, location_id, secret_id) +def test_access_regional_secret_version( + regional_secret_version: Tuple[str, str, str, str, str] +) -> None: + project_id, location_id, secret_id, version_id, _ = regional_secret_version + version = access_regional_secret_version( + project_id, location_id, secret_id, version_id + ) + assert version.payload.data == b"hello world!" + +def test_add_regional_secret_version( + regional_secret: Tuple[str, str, str, str] +) -> None: + project_id, location_id, secret_id, _ = regional_secret + payload = "test123" + version = add_regional_secret_version(project_id, location_id, secret_id, payload) + assert secret_id in version.name + def test_create_regional_secret( regional_client: secretmanager_v1.SecretManagerServiceClient, project_id: str, @@ -146,6 +199,42 @@ def test_delete_regional_secret( regional_client, request={"name": name} ) +def test_destroy_regional_secret_version( + regional_client: secretmanager_v1.SecretManagerServiceClient, + regional_secret_version: Tuple[str, str, str, str, str], +) -> None: + project_id, location_id, secret_id, version_id, _ = regional_secret_version + version = destroy_regional_secret_version( + project_id, location_id, secret_id, version_id + ) + assert version.destroy_time + +def test_enable_disable_regional_secret_version( + regional_client: secretmanager_v1.SecretManagerServiceClient, + regional_secret_version: Tuple[str, str, str, str, str], +) -> None: + project_id, location_id, secret_id, version_id, _ = regional_secret_version + version = disable_regional_secret_version( + project_id, location_id, secret_id, version_id + ) + assert version.state == secretmanager.SecretVersion.State.DISABLED + + version = enable_regional_secret_version( + project_id, location_id, secret_id, version_id + ) + assert version.state == secretmanager.SecretVersion.State.ENABLED + +def test_get_regional_secret_version( + regional_client: secretmanager_v1.SecretManagerServiceClient, + regional_secret_version: Tuple[str, str, str, str, str], +) -> None: + project_id, location_id, secret_id, version_id, _ = regional_secret_version + version = get_regional_secret_version( + project_id, location_id, secret_id, version_id + ) + assert secret_id in version.name + assert version_id in version.name + def test_update_regional_secret(regional_secret: Tuple[str, str, str, str]) -> None: project_id, location_id, secret_id, _ = regional_secret updated_regional_secret = update_regional_secret(project_id, location_id, secret_id)