Add copy-tree command

This commit is contained in:
Correl Roush 2024-01-23 12:59:49 -05:00
parent daf9120f5f
commit f5d48347e5

90
ssm.py
View file

@ -10,6 +10,7 @@ import botocore.client
import botocore.session import botocore.session
import rich.console import rich.console
import rich.table import rich.table
import rich.text
import typer import typer
app = typer.Typer() app = typer.Typer()
@ -39,6 +40,22 @@ def get_client(profile: str) -> botocore.client.BaseClient:
sys.exit(1) sys.exit(1)
def get_parameters(
client: botocore.client.BaseClient, path: SSMPath, recursive: bool
) -> typing.Iterable[dict]:
...
results = client.get_paginator("get_parameters_by_path").paginate(
Path=str(path),
Recursive=recursive,
WithDecryption=True,
)
for parameter in (
result for chunk in results for result in chunk.get("Parameters")
):
if parameter.get("Name"):
yield parameter
@app.command() @app.command()
def profiles() -> None: def profiles() -> None:
"""List available AWS profiles.""" """List available AWS profiles."""
@ -46,8 +63,8 @@ def profiles() -> None:
print(profile) print(profile)
@app.command() @app.command("list")
def list( def list_parameters(
profile: ProfileArg, profile: ProfileArg,
path: pathlib.Path, path: pathlib.Path,
recursive: bool = True, recursive: bool = True,
@ -63,21 +80,17 @@ def list(
"Type", "Type",
title=f"{root} ({profile})", title=f"{root} ({profile})",
) )
results = client.get_paginator("get_parameters_by_path").paginate( try:
Path=str(root), for parameter in get_parameters(client, root, recursive):
Recursive=recursive,
WithDecryption=True,
)
for parameter in (
result for chunk in results for result in chunk.get("Parameters")
):
if name := parameter.get("Name"):
table.add_row( table.add_row(
str(pathlib.Path(name).relative_to(root)), str(pathlib.Path(parameter["Name"]).relative_to(root)),
parameter.get("Value"), parameter.get("Value"),
parameter.get("Description"), parameter.get("Description"),
parameter.get("Type"), parameter.get("Type"),
) )
except client.exceptions.ClientError as e:
stderr.print(str(e), style="bold red")
sys.exit(1)
console.print(table) console.print(table)
@ -122,5 +135,58 @@ def unset(profile: ProfileArg, path: str) -> None:
sys.exit(1) sys.exit(1)
@app.command()
def copy_tree(
source_profile: ProfileArg,
source_path: str,
dest_profile: ProfileArg,
dest_path: str,
replacement_pairs: list[str] = typer.Option([], "--replace", "-r"),
recursive: bool = True,
):
"""Copy parameters from SRC_PATH to DEST_PATH."""
def parse_replacements(values: list[str]) -> list[tuple[str, str]]:
pairs: list[tuple[str, str]] = []
for value in values:
a, b = value.split("=", 1)
pairs.append((a, b))
return pairs
replacements = parse_replacements(replacement_pairs)
def replace(value: str) -> str:
for old, new in replacements:
value = value.replace(old, new)
return value
source = get_client(source_profile)
destination = get_client(dest_profile)
sources = {
str(pathlib.Path(p["Name"]).relative_to(SSMPath(source_path))): p
for p in get_parameters(source, SSMPath(source_path), recursive=recursive)
}
targets = {
str(pathlib.Path(p["Name"]).relative_to(SSMPath(source_path))): p
for p in get_parameters(destination, SSMPath(dest_path), recursive=recursive)
}
table = rich.table.Table("Path", "Old Value", "New Value")
for name, param in sources.items():
old = targets[name]["Value"] if name in targets else None
new = replace(param["Value"])
table.add_row(
name,
rich.text.Text(old, style="red")
if old
else rich.text.Text("Not defined", style="bright_black italic"),
rich.text.Text(new, style="green"),
)
stdout.print(table)
confirmed = typer.confirm("Are you sure you want to apply the above changes?")
if not confirmed:
stderr.print("No changes applied", style="yellow italic")
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
app() app()