Skip to content

Commit

Permalink
Make the --requirements param to accept file and text
Browse files Browse the repository at this point in the history
closes pulp#230
  • Loading branch information
lubosmj committed Dec 1, 2022
1 parent 70b6979 commit 63dbee5
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 90 deletions.
1 change: 1 addition & 0 deletions CHANGES/230.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Made the `--requirements` option for ansible remotes to accept both files and strings.
3 changes: 2 additions & 1 deletion pulpcore/cli/ansible/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
href_option,
label_command,
list_command,
load_file_or_string_callback,
name_option,
pass_pulp_context,
pulp_group,
Expand Down Expand Up @@ -76,7 +77,7 @@ def remote(ctx: click.Context, pulp_ctx: PulpCLIContext, remote_type: str) -> No
),
pulp_option(
"--requirements",
callback=_requirements_callback,
callback=load_file_or_string_callback(lambda c, p, x: f"{yaml.safe_load(x)}"),
help=_("Collections only: a string of a requirements yaml"),
allowed_with_contexts=collection_context,
),
Expand Down
12 changes: 7 additions & 5 deletions pulpcore/cli/ansible/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
GroupOption,
PulpCLIContext,
create_command,
create_content_json_callback,
create_content_json_handler,
destroy_command,
href_option,
json_handler,
label_command,
label_select_option,
list_command,
load_file_or_string_callback,
load_json_callback,
name_option,
pass_pulp_context,
pass_repository_context,
Expand Down Expand Up @@ -109,7 +109,7 @@ def repository(ctx: click.Context, pulp_ctx: PulpCLIContext, repo_type: str) ->
click.option("--description"),
pulp_option(
"--gpgkey",
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
needs_plugins=[
PluginRequirement(
"ansible", min="0.15.0.dev", feature="gpgkeys on ansible repositories"
Expand Down Expand Up @@ -146,7 +146,9 @@ def repository(ctx: click.Context, pulp_ctx: PulpCLIContext, repo_type: str) ->
),
href_option,
]
content_json_callback = create_content_json_callback(schema=CONTENT_LIST_SCHEMA)
content_json_callback = load_file_or_string_callback(
create_content_json_handler(schema=CONTENT_LIST_SCHEMA)
)
modify_options = [
click.option(
"--add-content",
Expand Down Expand Up @@ -227,7 +229,7 @@ def sync(
@name_option
@href_option
@click.option("--signing-service", required=True, callback=_signing_service_callback)
@click.option("--content-units", callback=load_json_callback)
@click.option("--content-units", callback=load_file_or_string_callback(json_handler))
@pass_repository_context
def sign(
repository_ctx: PulpRepositoryContext,
Expand Down
5 changes: 3 additions & 2 deletions pulpcore/cli/common/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from pulpcore.cli.common.context import PluginRequirement
from pulpcore.cli.common.generic import (
PulpCLIContext,
load_json_callback,
json_handler,
load_file_or_string_callback,
pass_pulp_context,
pulp_group,
)
Expand Down Expand Up @@ -100,7 +101,7 @@ def operation_ids(pulp_ctx: PulpCLIContext) -> None:
@openapi_group.command()
@click.option("--id", "operation_id", required=True, help=_("Operation ID in openapi schema"))
@click.option("--parameter", "parameters", multiple=True)
@click.option("--body", callback=load_json_callback)
@click.option("--body", callback=load_file_or_string_callback(json_handler))
@click.option("--upload", "uploads", type=click.File("rb"), multiple=True)
@pass_pulp_context
def call(
Expand Down
85 changes: 35 additions & 50 deletions pulpcore/cli/common/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,74 +328,59 @@ def _version_callback(
return value


# TODO: would be great to have enable this to take a validator, rather than having
# to build "on top of" it like I'm doing now w/ json_callback
def load_file_or_string_callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
handler: Callable[[click.Context, click.Parameter, str], Any] = lambda c, p, x: x
) -> Any:
"""Load string from input or from file if string starts with @."""
the_content: str

# pass None and "" verbatim
if not value:
return value

if value.startswith("@"):
the_file = value[1:]
try:
with click.open_file(the_file, "r") as fp:
the_content = fp.read()
except OSError:
raise click.ClickException(
_("Failed to load content from {file}").format(file=the_file)
)
else:
the_content = value
def _load_file_or_string_callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Any:
"""Load the string from input, or from file if the value starts with @."""
if not value:
return value

return the_content
if value.startswith("@"):
the_file = value[1:]
try:
with click.open_file(the_file, "r") as fp:
the_content = fp.read()
except OSError:
raise click.ClickException(
_("Failed to load content from {file}").format(file=the_file)
)
else:
the_content = value

return handler(ctx, param, the_content)

def load_json_callback(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Any:
"""Load JSON from input string or from file if string starts with @."""
return _load_file_or_string_callback

# None or empty-str are legal - shortcircuit here
if not value:
return value

# Now try to evaluate legal JSON
json_object: Any
json_string: str = load_file_or_string_callback(ctx, param, value)
def json_handler(ctx: click.Context, param: click.Parameter, value: str) -> Any:
try:
json_object = json.loads(json_string)
json_object = json.loads(value)
except json.decoder.JSONDecodeError:
raise click.ClickException(_("Failed to decode JSON"))
else:
return json_object


def labels_callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Optional[Dict[str, str]]:
# None is legal - shortcircuit here
if value is None:
return value

value = load_json_callback(ctx, param, value)
def labels_handler(ctx: click.Context, param: click.Parameter, value: str) -> Any:
value = json_handler(ctx, param, value)
if isinstance(value, dict) and all(
(isinstance(key, str) and isinstance(val, str) for key, val in value.items())
):
return value
raise click.ClickException(_("Labels must be provided as a dictionary of strings."))


def create_content_json_callback(
def create_content_json_handler(
context_class: Optional[Type[PulpContentContext]] = None, schema: s.Schema = None
) -> Any:
def _callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
ctx: click.Context, param: click.Parameter, value: str
) -> Optional[List[PulpContentContext]]:
ctx_class = context_class
new_value = load_json_callback(ctx, param, value)
new_value = json_handler(ctx, param, value)
if new_value is not None:
if schema is not None:
try:
Expand Down Expand Up @@ -724,7 +709,7 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
"Search for {entities} with these content hrefs in them (JSON list or "
"@file containing a JSON list)"
),
callback=load_json_callback,
callback=load_file_or_string_callback(json_handler),
)

chunk_size_option = pulp_option(
Expand Down Expand Up @@ -775,7 +760,7 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
help=_(
"JSON dictionary of labels to set on {entity} (or " "@file containing a JSON dictionary)"
),
callback=labels_callback,
callback=load_file_or_string_callback(labels_handler),
)

name_filter_options = [
Expand Down Expand Up @@ -816,17 +801,17 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
click.option(
"--ca-cert",
help=_("a PEM encoded CA certificate or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option(
"--client-cert",
help=_("a PEM encoded client certificate or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option(
"--client-key",
help=_("a PEM encode private key or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option("--connect-timeout", type=float),
click.option(
Expand Down Expand Up @@ -866,17 +851,17 @@ def _type_callback(ctx: click.Context, param: click.Parameter, value: Optional[s
click.option(
"--ca-cert",
help=_("a PEM encoded CA certificate or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option(
"--client-cert",
help=_("a PEM encoded client certificate or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option(
"--client-key",
help=_("a PEM encode private key or @file containing same"),
callback=load_file_or_string_callback,
callback=load_file_or_string_callback(),
),
click.option("--connect-timeout", type=float_or_empty),
click.option(
Expand Down
7 changes: 4 additions & 3 deletions pulpcore/cli/container/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
create_command,
destroy_command,
href_option,
json_handler,
label_command,
list_command,
load_json_callback,
load_file_or_string_callback,
name_option,
pass_pulp_context,
pulp_group,
Expand Down Expand Up @@ -47,8 +48,8 @@ def remote(ctx: click.Context, pulp_ctx: PulpCLIContext, remote_type: str) -> No
click.option(
"--policy", type=click.Choice(["immediate", "on_demand", "streamed"], case_sensitive=False)
),
click.option("--include-tags", callback=load_json_callback),
click.option("--exclude-tags", callback=load_json_callback),
click.option("--include-tags", callback=load_file_or_string_callback(json_handler)),
click.option("--exclude-tags", callback=load_file_or_string_callback(json_handler)),
]
remote_create_options = (
common_remote_create_options + remote_options + [click.option("--upstream-name", required=True)]
Expand Down
7 changes: 4 additions & 3 deletions pulpcore/cli/core/access_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from pulpcore.cli.common.generic import (
PulpCLIContext,
href_option,
json_handler,
list_command,
load_json_callback,
load_file_or_string_callback,
lookup_callback,
pass_entity_context,
pass_pulp_context,
Expand Down Expand Up @@ -32,8 +33,8 @@ def access_policy(ctx: click.Context, pulp_ctx: PulpCLIContext) -> None:
]

update_options = [
click.option("--statements", callback=load_json_callback),
click.option("--creation-hooks", callback=load_json_callback),
click.option("--statements", callback=load_file_or_string_callback(json_handler)),
click.option("--creation-hooks", callback=load_file_or_string_callback(json_handler)),
]

access_policy.add_command(list_command())
Expand Down
5 changes: 3 additions & 2 deletions pulpcore/cli/core/orphan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from pulpcore.cli.common.context import PluginRequirement
from pulpcore.cli.common.generic import (
PulpCLIContext,
load_json_callback,
json_handler,
load_file_or_string_callback,
pass_pulp_context,
pulp_group,
pulp_option,
Expand All @@ -26,7 +27,7 @@ def orphan() -> None:
@pulp_option(
"--content-hrefs",
help=_("List of specific Contents to delete if they are orphans"),
callback=load_json_callback,
callback=load_file_or_string_callback(json_handler),
needs_plugins=[PluginRequirement("core", "3.14.0")],
)
@pulp_option(
Expand Down
19 changes: 9 additions & 10 deletions pulpcore/cli/file/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
GroupOption,
PulpCLIContext,
create_command,
create_content_json_callback,
create_content_json_handler,
destroy_command,
href_option,
json_handler,
label_command,
label_select_option,
list_command,
load_json_callback,
load_file_or_string_callback,
name_option,
pass_pulp_context,
pass_repository_context,
Expand Down Expand Up @@ -72,10 +73,8 @@ def _content_callback(ctx: click.Context, param: click.Parameter, value: Any) ->
CONTENT_LIST_SCHEMA = s.Schema([{"sha256": str, "relative_path": s.And(str, len)}])


def _content_list_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
result = load_json_callback(ctx, param, value)
if result is None:
return None
def _content_list_callback(ctx: click.Context, param: click.Parameter, value: str) -> Any:
result = json_handler(ctx, param, value)
try:
return CONTENT_LIST_SCHEMA.validate(result)
except s.SchemaError as e:
Expand Down Expand Up @@ -128,8 +127,8 @@ def repository(ctx: click.Context, pulp_ctx: PulpCLIContext, repo_type: str) ->
callback=_content_callback,
),
]
content_json_callback = create_content_json_callback(
PulpFileContentContext, schema=CONTENT_LIST_SCHEMA
content_json_callback = load_file_or_string_callback(
create_content_json_handler(PulpFileContentContext, schema=CONTENT_LIST_SCHEMA)
)
modify_options = [
click.option(
Expand Down Expand Up @@ -278,7 +277,7 @@ def remove(
@click.option(
"--add-content",
default="[]",
callback=_content_list_callback,
callback=load_file_or_string_callback(_content_list_callback),
expose_value=True,
help=_(
"""JSON string with a list of objects to add to the repository.
Expand All @@ -289,7 +288,7 @@ def remove(
@click.option(
"--remove-content",
default="[]",
callback=_content_list_callback,
callback=load_file_or_string_callback(_content_list_callback),
expose_value=True,
help=_(
"""JSON string with a list of objects to remove from the repository.
Expand Down
5 changes: 3 additions & 2 deletions pulpcore/cli/migration/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
create_command,
destroy_command,
href_option,
json_handler,
list_command,
load_json_callback,
load_file_or_string_callback,
pass_entity_context,
pass_pulp_context,
pulp_group,
Expand Down Expand Up @@ -34,7 +35,7 @@ def plan(ctx: click.Context, pulp_ctx: PulpCLIContext) -> None:
click.option(
"--plan",
required=True,
callback=load_json_callback,
callback=load_file_or_string_callback(json_handler),
help=_(
"Migration plan in json format. The argument can be prefixed with @ to use a file "
"containing the json."
Expand Down
Loading

0 comments on commit 63dbee5

Please sign in to comment.