Skip to content

Commit

Permalink
Added new content subgroup to repository commands
Browse files Browse the repository at this point in the history
fixes: pulp#171
closes: pulp#215
  • Loading branch information
gerrod3 committed Apr 28, 2021
1 parent d2be887 commit 1b1133e
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES/171.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Repository content commands are now nested under a new content subgroup.
1 change: 1 addition & 0 deletions CHANGES/215.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Repository add/remove/modify commands have now been deprecated. Please use the new content subgroup commands.
11 changes: 11 additions & 0 deletions pulpcore/cli/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,16 @@ def modify(
)


class PulpContentContext(PulpEntityContext):
"""
Base class for content specific contexts
"""

ENTITY = "content"
ENTITIES = "content"
LIST_ID = "content_list"


EntityFieldDefinition = Union[None, str, PulpEntityContext]
##############################################################################
# Decorator to access certain contexts
Expand All @@ -566,3 +576,4 @@ def modify(
pass_entity_context = click.make_pass_decorator(PulpEntityContext)
pass_repository_context = click.make_pass_decorator(PulpRepositoryContext)
pass_repository_version_context = click.make_pass_decorator(PulpRepositoryVersionContext)
pass_content_context = click.make_pass_decorator(PulpContentContext)
155 changes: 153 additions & 2 deletions pulpcore/cli/common/generic.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import gettext
import json
from typing import Any, NamedTuple, Optional, Tuple, Union
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Type, Union

import click

from pulpcore.cli.common.context import (
DEFAULT_LIMIT,
EntityDefinition,
PulpContentContext,
PulpContext,
PulpEntityContext,
PulpRepositoryContext,
PulpRepositoryVersionContext,
pass_content_context,
pass_entity_context,
pass_pulp_context,
pass_repository_context,
Expand Down Expand Up @@ -47,6 +49,32 @@ def get_help_record(self, ctx: click.Context) -> Tuple[str, str]:
return synopsis, help_text


class GroupOption(PulpOption):
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.group: List[str] = kwargs.pop("group")
assert self.group, "'group' parameter required"
kwargs["help"] = (
kwargs.get("help", "") + "Option is grouped with " + ", ".join(self.group) + "."
).strip()
super().__init__(*args, **kwargs)

def handle_parse_result(self, ctx: click.Context, opts: Dict[str, Any], args: Any) -> Any:
all_options = self.group + [self.name]
if all(x in opts for x in all_options):
self.prompt = None # type: ignore
else:
raise click.UsageError(
"Illegal usage, please specify all "
"options in the group: " + ", ".join(all_options)
)
value = opts.get(self.name)
if self.callback is not None:
value = self.callback(ctx, self, {o: opts[o] for o in all_options}) # type: ignore
if self.expose_value:
ctx.params[self.name] = value
return value, args


##############################################################################
# Option callbacks

Expand Down Expand Up @@ -102,7 +130,7 @@ def _version_callback(
def load_json_callback(
ctx: click.Context,
param: click.Parameter,
value: str,
value: Optional[str],
) -> Any:
"""Load JSON from input string or from file if string starts with @."""
json_object: Any
Expand All @@ -129,6 +157,19 @@ def load_json_callback(
return json_object


def create_content_json_callback(content_ctx: Type[PulpContentContext]) -> Any:
def _callback(
ctx: click.Context, param: click.Parameter, value: Optional[str]
) -> Optional[List[PulpContentContext]]:
new_value = load_json_callback(ctx, param, value)
if new_value is not None:
pulp_ctx: PulpContext = ctx.find_object(PulpContext)
return [content_ctx(pulp_ctx, entity=unit) for unit in new_value]
return new_value

return _callback


##############################################################################
# Decorator common options

Expand Down Expand Up @@ -435,3 +476,113 @@ def label_show(entity_ctx: PulpEntityContext, key: str) -> None:
label_group.add_command(subcmd)

return label_group


def repository_content_command(**kwargs: Any) -> click.Group:
"""A factory that creates a repository content command group."""

content_contexts = kwargs.pop("contexts", {})
names = list(content_contexts.keys()) + ["all"]
content_contexts.update({"all": PulpContentContext})

def version_callback(
ctx: click.Context, param: click.Parameter, value: Optional[int]
) -> PulpRepositoryVersionContext:
repo_ctx: PulpRepositoryContext = ctx.find_object(PulpRepositoryContext)
repo_ver_ctx = repo_ctx.get_version_context()
repo_ver_ctx.pulp_href = (
f"{repo_ctx.pulp_href}versions/{value}/"
if value
else repo_ver_ctx.entity["latest_version_href"]
)
return repo_ver_ctx

@click.command("list")
@click.option("-t", "--type", "type", type=click.Choice(names), default=names[0])
@limit_option
@offset_option
@repository_option
@click.option("--version", type=int, callback=version_callback)
@pass_pulp_context
def content_list(
pulp_ctx: PulpContext,
version: PulpRepositoryVersionContext,
offset: Optional[int],
limit: Optional[int],
type: Optional[str],
**params: Any,
) -> None:
if type == "all":
pulp_ctx.needs_plugin("core", "3.11.0")
parameters = {k: v for k, v in params.items() if v is not None}
parameters.update({"repository_version": version.pulp_href})
result = content_contexts[type](pulp_ctx).list(
limit=limit, offset=offset, parameters=parameters
)
pulp_ctx.output_result(result)

@click.command("add")
@repository_option
@click.option("--base-version", type=int, callback=version_callback)
@pass_content_context
def content_add(
content_ctx: PulpContentContext,
base_version: PulpRepositoryVersionContext,
) -> None:
repo_ctx = base_version.repository_ctx
repo_ctx.modify(
repo_ctx.pulp_href,
add_content=[content_ctx.pulp_href],
base_version=base_version.pulp_href,
)

@click.command("remove")
@click.option("--all", is_flag=True, help=_("Remove all content from repository version"))
@repository_option
@click.option("--base-version", type=int, callback=version_callback)
@pass_content_context
def content_remove(
content_ctx: PulpContentContext,
base_version: PulpRepositoryVersionContext,
all: bool,
) -> None:
repo_ctx = base_version.repository_ctx
remove_content = ["*" if all else content_ctx.pulp_href]
repo_ctx.modify(
repo_ctx.pulp_href, remove_content=remove_content, base_version=base_version.pulp_href
)

@click.command("modify")
@repository_option
@click.option("--base-version", type=int, callback=version_callback)
def content_modify(
base_version: PulpRepositoryVersionContext,
add_content: Optional[List[PulpContentContext]],
remove_content: Optional[List[PulpContentContext]],
) -> None:
repo_ctx = base_version.repository_ctx
ac = [unit.pulp_href for unit in add_content] if add_content else None
rc = [unit.pulp_href for unit in remove_content] if remove_content else None
repo_ctx.modify(repo_ctx.pulp_href, ac, rc, base_version.pulp_href)

command_decorators = {
content_list: kwargs.pop("list_decorators", []),
content_add: kwargs.pop("add_decorators", []),
content_remove: kwargs.pop("remove_decorators", []),
content_modify: kwargs.pop("modify_decorators", []),
}
if not kwargs.get("name"):
kwargs["name"] = "content"

@click.group(**kwargs)
@pass_pulp_context
@click.pass_context
def content_group(ctx: click.Context, pulp_ctx: PulpContext) -> None:
ctx.obj = PulpContentContext(pulp_ctx)

for command, options in command_decorators.items():
for option in options:
command = option(command)
content_group.add_command(command)

return content_group
2 changes: 1 addition & 1 deletion pulpcore/cli/core/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def list(
exporter: str,
limit: int,
offset: int,
**kwargs: Any
**kwargs: Any,
) -> None:
params = {k: v for k, v in kwargs.items() if v is not None}
exporter_ctx = PulpExporterContext(pulp_ctx)
Expand Down
3 changes: 2 additions & 1 deletion pulpcore/cli/file/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pulpcore.cli.common.context import (
EntityDefinition,
PulpContentContext,
PulpEntityContext,
PulpRemoteContext,
PulpRepositoryContext,
Expand All @@ -11,7 +12,7 @@
_ = gettext.gettext


class PulpFileContentContext(PulpEntityContext):
class PulpFileContentContext(PulpContentContext):
ENTITY = "file content"
ENTITIES = "file content"
HREF = "file_file_content_href"
Expand Down
60 changes: 56 additions & 4 deletions pulpcore/cli/file/repository.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import gettext
from typing import Dict, List, Optional, Union
from typing import Any, Dict, List, Optional, Union

import click

Expand All @@ -11,14 +11,17 @@
pass_repository_context,
)
from pulpcore.cli.common.generic import (
GroupOption,
create_command,
create_content_json_callback,
destroy_command,
href_option,
label_command,
label_select_option,
list_command,
load_json_callback,
name_option,
repository_content_command,
repository_href_option,
repository_option,
show_command,
Expand All @@ -45,6 +48,13 @@ def _remote_callback(
return value


def _content_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Optional[str]:
if value:
pulp_ctx: PulpContext = ctx.find_object(PulpContext)
ctx.obj = PulpFileContentContext(pulp_ctx, entity=value)
return str(value)


@click.group()
@click.option(
"-t",
Expand Down Expand Up @@ -73,6 +83,37 @@ def repository(ctx: click.Context, pulp_ctx: PulpContext, repo_type: str) -> Non
click.option("--description"),
click.option("--remote", callback=_remote_callback),
]
file_options = [
click.option("--sha256", cls=GroupOption, expose_value=False, group=["relative_path"]),
click.option(
"--relative-path",
cls=GroupOption,
expose_value=False,
group=["sha256"],
callback=_content_callback,
),
]
content_json_callback = create_content_json_callback(PulpFileContentContext)
modify_options = [
click.option(
"--add-content",
callback=content_json_callback,
help=_(
"""JSON string with a list of objects to add to the repository.
Each object should consist of the following keys: "sha256", "relative_path"..
The argument prefixed with the '@' can be the path to a JSON file with a list of objects."""
),
),
click.option(
"--remove-content",
callback=content_json_callback,
help=_(
"""JSON string with a list of objects to remove from the repository.
Each object should consist of the following keys: "sha256", "relative_path"..
The argument prefixed with the '@' can be the path to a JSON file with a list of objects."""
),
),
]

repository.add_command(list_command(decorators=[label_select_option]))
repository.add_command(show_command(decorators=lookup_options))
Expand All @@ -82,6 +123,14 @@ def repository(ctx: click.Context, pulp_ctx: PulpContext, repo_type: str) -> Non
repository.add_command(task_command(decorators=nested_lookup_options))
repository.add_command(version_command(decorators=nested_lookup_options))
repository.add_command(label_command(decorators=nested_lookup_options))
repository.add_command(
repository_content_command(
contexts={"file": PulpFileContentContext},
add_decorators=file_options,
remove_decorators=file_options,
modify_decorators=modify_options,
)
)


@repository.command()
Expand Down Expand Up @@ -112,7 +161,7 @@ def sync(
)


@repository.command()
@repository.command(deprecated=True)
@name_option
@href_option
@click.option("--sha256", required=True)
Expand All @@ -127,6 +176,7 @@ def add(
relative_path: str,
base_version: Optional[int],
) -> None:
"""Please use 'content add' instead."""
repository_href = repository_ctx.pulp_href

base_version_href: Optional[str]
Expand All @@ -146,7 +196,7 @@ def add(
)


@repository.command()
@repository.command(deprecated=True)
@name_option
@href_option
@click.option("--sha256", required=True)
Expand All @@ -161,6 +211,7 @@ def remove(
relative_path: str,
base_version: Optional[int],
) -> None:
"""Please use 'content remove' instead."""
repository_href = repository_ctx.pulp_href

base_version_href: Optional[str]
Expand All @@ -180,7 +231,7 @@ def remove(
)


@repository.command()
@repository.command(deprecated=True)
@name_option
@href_option
@click.option("--base-version", type=int)
Expand Down Expand Up @@ -215,6 +266,7 @@ def modify(
remove_content: List[Dict[str, str]],
base_version: Optional[int],
) -> None:
"""Please use 'content modify' instead."""
repository_href = repository_ctx.pulp_href

base_version_href: Optional[str]
Expand Down
Loading

0 comments on commit 1b1133e

Please sign in to comment.