Skip to content
Open
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
62 changes: 46 additions & 16 deletions nmdc_server/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,74 @@
def swap_gcp_secret_values(gcp_project_id: str, secret_a_id: str, secret_b_id: str) -> None:
"""Swaps the values of two secrets in Google Secret Manager.
Note: To update a secret's content, we "add a version" of that secret.
Note: To update a given secret's value, we disable the latest "version" of that secret,
then we add a new "version" (enabled by default) of the secret. The new "version"
contains the new value.
TODO: Consider having both versions of the secret pre-created on GCP,
and then—here—just activate one version or the other. That could
make it so we aren't storing so many versions of each secret.
Note: ⚠️ This algorithm leaves a gap in time during which no versions of the secret
are enabled. This was easier for me to think about than having _both_ versions
of the same secret be enabled simultaneously, which I think would be harder to
detect downstream (I'd prefer an error versus silent continuation with a stale
value). Either way, Google Secret Manager does not offer an atomic swap operation.
TODO: Consider storing both values within the _same_ secret so that they can be
swapped atomically. This is analogous to how things are done on Rancher/Spin,
although—there—there's only one level of secret storage (i.e. Kubernetes) as
opposed to two (i.e. Kubernetes and Google Secret Manager).
Note: The "Add version" form on the Google Secret Manager console (web UI) has
a checkbox that can be used to disable all previous versions of the secret.
I am not aware of a corresponding kwarg to the `add_secret_version` method.
References (note: the `noqa` comment prevents the linter from flagging the line length):
- Importing the Python library: https://cloud.google.com/secret-manager/docs/reference/libraries#client-libraries-install-python # noqa: E501
- Add secret version: https://cloud.google.com/secret-manager/docs/samples/secretmanager-add-secret-version # noqa: E501
- Disable secret version: https://cloud.google.com/secret-manager/docs/samples/secretmanager-disable-secret-version # noqa: E501
"""

client = secretmanager.SecretManagerServiceClient()

# Get the initial value of the first secret.
# Get the value of the latest "version" of the first secret.
secret_a_path = client.secret_path(gcp_project_id, secret_a_id)
request = secretmanager.AccessSecretVersionRequest(name=f"{secret_a_path}/versions/latest")
secret_a_latest_path = f"{secret_a_path}/versions/latest"
request = secretmanager.AccessSecretVersionRequest(name=secret_a_latest_path)
response = client.access_secret_version(request=request)
secret_a_resolved_path: str = response.name # ".../latest" is resolved to, e.g., ".../123"
secret_a_value: bytes = response.payload.data
click.echo(f"Read secret: {secret_a_path}")
click.echo(f"Read secret version: {secret_a_resolved_path}")

# Get the initial value of the second secret.
# Get the value of the latest "version" of the second secret.
secret_b_path = client.secret_path(gcp_project_id, secret_b_id)
request = secretmanager.AccessSecretVersionRequest(name=f"{secret_b_path}/versions/latest")
secret_b_latest_path = f"{secret_b_path}/versions/latest"
request = secretmanager.AccessSecretVersionRequest(name=secret_b_latest_path)
response = client.access_secret_version(request=request)
secret_b_resolved_path: str = response.name # ".../latest" is resolved to, e.g., ".../456"
secret_b_value: bytes = response.payload.data
click.echo(f"Read secret: {secret_b_path}")
click.echo(f"Read secret version: {secret_b_resolved_path}")

# Disable that "version" of the first secret.
request = secretmanager.DisableSecretVersionRequest(name=secret_a_resolved_path)
_ = client.disable_secret_version(request=request)
click.echo(f"Disabled secret version: {secret_a_resolved_path}")

# Disable that "version" of the second secret.
request = secretmanager.DisableSecretVersionRequest(name=secret_b_resolved_path)
_ = client.disable_secret_version(request=request)
click.echo(f"Disabled secret version: {secret_b_resolved_path}")

# Put the second secret's initial value into the first secret.
# Add a "version" to the first secret, containing the value from the second secret.
payload = secretmanager.SecretPayload(data=secret_b_value)
request = secretmanager.AddSecretVersionRequest(parent=secret_a_path, payload=payload)
_ = client.add_secret_version(request=request)
click.echo(f"Updated secret: {secret_a_path}")
response = client.add_secret_version(request=request)
added_secret_a_resolved_path: str = response.name
click.echo(f"Added secret version: {added_secret_a_resolved_path}")

# Put the first secret's initial value into the second secret.
# Add a "version" to the second secret, containing the value from the first secret.
payload = secretmanager.SecretPayload(data=secret_a_value)
request = secretmanager.AddSecretVersionRequest(parent=secret_b_path, payload=payload)
_ = client.add_secret_version(request=request)
click.echo(f"Updated secret: {secret_b_path}")
response = client.add_secret_version(request=request)
added_secret_b_resolved_path: str = response.name
click.echo(f"Added secret version: {added_secret_b_resolved_path}")


def swap_rancher_secret_values(
Expand Down
Loading