Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add settings --schema support #1093

Merged
merged 12 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ repos:
- astroid == 2.9.0
- ansible-runner
- jinja2
- jsonschema
- libtmux
- onigurumacffi
- pytest
Expand Down
7 changes: 7 additions & 0 deletions docs/changelog-fragments.d/1093.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Added the ability to produce a json schema file for the settings file.

```bash
ansible-navigator settings --json-schema
cidrblock marked this conversation as resolved.
Show resolved Hide resolved
```

-- by {user}`cidrblock`
4 changes: 4 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ ignore_missing_imports = true
# No type hints as of version 2.1.2
ignore_missing_imports = true

[mypy-jsonschema.*]
# No type hints as of version 4.4.0
ignore_missing_imports = true

[mypy-libtmux]
# No type hints as of version 0.10.3
ignore_missing_imports = true
Expand Down
6 changes: 6 additions & 0 deletions src/ansible_navigator/actions/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from dataclasses import asdict
from typing import Tuple

from ansible_navigator.configuration_subsystem.transform import to_schema
cidrblock marked this conversation as resolved.
Show resolved Hide resolved
from ..action_base import ActionBase
from ..action_defs import RunStdoutReturn
from ..app_public import AppPublic
Expand Down Expand Up @@ -116,6 +117,11 @@ def run_stdout(self) -> RunStdoutReturn:
:returns: RunStdoutReturn
"""
self._logger.debug("settings requested in stdout mode")
if self._args.json_schema:
schema = to_schema(self._args)
print(schema)
return RunStdoutReturn(message="", return_code=0)

self._settings = to_presentable(self._args)
info_dump = serialize(
content=list(self._settings),
Expand Down
2 changes: 2 additions & 0 deletions src/ansible_navigator/configuration_subsystem/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .defs_presentable import PresentableSettingsEntry
from .navigator_configuration import NavigatorConfiguration
from .transform import to_presentable
from .transform import to_schema


__all__ = (
Expand All @@ -19,4 +20,5 @@
"PresentableSettingsEntries",
"SettingsEntry",
"to_presentable",
"to_schema",
)
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@ class Internals:
subcommands=["inventory", "run"],
value=SettingsEntryValue(),
),
SettingsEntry(
name="json_schema",
choices=[True, False],
cli_parameters=CliParameters(short="--js", action="store_true"),
settings_file_path_override="settings.json-schema",
short_description="Generate a json schema for the settings file",
cidrblock marked this conversation as resolved.
Show resolved Hide resolved
subcommands=["settings"],
value=SettingsEntryValue(default=False),
),
SettingsEntry(
name="log_append",
choices=[True, False],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,13 @@ def exec_shell(
return self._true_or_false(entry, config)

@_post_processor
def _help_for_command(
def _forced_stdout(
self,
entry: SettingsEntry,
config: ApplicationConfiguration,
subcommand: str,
) -> PostProcessorReturn:
"""Post process help_xxxx
"""Force mode stdout for a settings parameter.

:param entry: The current settings entry
:param config: The full application configuration
Expand All @@ -452,11 +452,11 @@ def _help_for_command(
messages.append(LogMessage(level=logging.DEBUG, message=message))
return messages, exit_messages

help_builder = partialmethod(_help_for_command, subcommand="builder")
help_config = partialmethod(_help_for_command, subcommand="config")
help_doc = partialmethod(_help_for_command, subcommand="doc")
help_inventory = partialmethod(_help_for_command, subcommand="inventory")
help_playbook = partialmethod(_help_for_command, subcommand="run")
help_builder = partialmethod(_forced_stdout, subcommand="builder")
help_config = partialmethod(_forced_stdout, subcommand="config")
help_doc = partialmethod(_forced_stdout, subcommand="doc")
help_inventory = partialmethod(_forced_stdout, subcommand="inventory")
help_playbook = partialmethod(_forced_stdout, subcommand="run")

@staticmethod
@_post_processor
Expand Down Expand Up @@ -498,6 +498,8 @@ def inventory_column(
entry.value.current = flatten_list(entry.value.current)
return messages, exit_messages

json_schema = partialmethod(_forced_stdout, subcommand="settings")

@_post_processor
def log_append(
self,
Expand Down
232 changes: 232 additions & 0 deletions src/ansible_navigator/configuration_subsystem/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
"""Partial json schema for settings."""
from typing import Dict


PARTIAL_SCHEMA: Dict = {
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": False,
"properties": {
"ansible-navigator": {
"properties": {
"ansible": {
"additionalProperties": False,
"properties": {
"cmdline": {
"type": "string",
},
"config": {
"type": "string",
},
"inventories": {
"items": {"type": "string"},
"type": "array",
},
"playbook": {"type": "string"},
},
"type": "object",
},
"ansible-builder": {
"additionalProperties": False,
"type": "object",
"properties": {
"workdir": {
"type": "string",
},
},
},
"ansible-runner": {
"additionalProperties": False,
"properties": {
"artifact-dir": {
"type": "string",
},
"rotate-artifacts-count": {
"type": "integer",
},
"timeout": {
"type": "integer",
},
},
"type": "object",
},
"app": {
"type": "string",
},
"collection-doc-cache-path": {
"type": "string",
},
"color": {
"additionalProperties": False,
"properties": {
"enable": {
"type": "boolean",
},
"osc4": {
"type": "boolean",
},
},
"type": "object",
},
"documentation": {
"additionalProperties": False,
"properties": {
"plugin": {
"additionalProperties": False,
"properties": {
"name": {
"type": "string",
},
"type": {
"type": "string",
},
},
"type": "object",
},
},
"type": "object",
},
"editor": {
"additionalProperties": False,
"properties": {
"command": {
"type": "string",
},
"console": {
"type": "boolean",
},
},
"type": "object",
},
"exec": {
"additionalProperties": False,
"properties": {
"command": {
"type": "string",
},
"shell": {
"type": "boolean",
},
},
"type": "object",
},
"execution-environment": {
"additionalProperties": False,
"properties": {
"container-engine": {
"type": "string",
},
"container-options": {
"items": {"type": "string"},
"type": "array",
},
"enabled": {
"type": "boolean",
},
"environment-variables": {
"additionalProperties": False,
"properties": {
"pass": {
"items": {"type": "string"},
"type": "array",
},
"set": {
"type": "object",
},
},
"type": "object",
},
"image": {
"type": "string",
},
"pull": {
"additionalProperties": False,
"properties": {
"arguments": {
"items": {"type": "string"},
"type": "array",
},
"policy": {
"type": "string",
},
},
},
"volume-mounts": {
"additionalProperties": False,
"properties": {
"dest": {"type": "string"},
"label": {"type": "string"},
"options": {"type": "string"},
},
"required": ["src", "dest"],
"type": "array",
},
},
"type": "object",
},
"help-builder": {
"type": "boolean",
},
"help-config": {
"type": "boolean",
},
"help-doc": {
"type": "boolean",
},
"help-inventory": {
"type": "boolean",
},
"help-playbook": {
"type": "boolean",
},
"inventory-columns": {
"items": {"type": "string"},
"type": "array",
},
"logging": {
"additionalProperties": False,
"properties": {
"append": {
"type": "boolean",
},
"file": {
"type": "string",
},
"level": {
"type": "string",
},
},
"type": "object",
},
"mode": {
"type": "string",
},
"playbook-artifact": {
"additionalProperties": False,
"properties": {
"enable": {
"type": "boolean",
},
"replay": {
"type": "string",
},
"save-as": {
"type": "string",
},
},
"type": "object",
},
"time-zone": {
"type": "string",
},
"settings": {
"additionalProperties": False,
"properties": {"json-schema": {"type": "boolean"}},
},
},
"additionalProperties": False,
},
},
"required": ["ansible-navigator"],
"title": "ansible-navigator settings file schema",
"type": "object",
}
31 changes: 31 additions & 0 deletions src/ansible_navigator/configuration_subsystem/transform.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
"""Methods of transforming the settings."""

from typing import Dict

from ..content_defs import ContentView
from ..utils.serialize import SerializationFormat
from ..utils.serialize import serialize
from .definitions import ApplicationConfiguration
from .definitions import Constants
from .defs_presentable import PresentableSettingsEntries
from .defs_presentable import PresentableSettingsEntry
from .schema import PARTIAL_SCHEMA


def to_presentable(settings: ApplicationConfiguration) -> PresentableSettingsEntries:
Expand Down Expand Up @@ -34,3 +41,27 @@ def to_presentable(settings: ApplicationConfiguration) -> PresentableSettingsEnt

settings_list.sort()
return PresentableSettingsEntries(tuple(settings_list))


def to_schema(settings: ApplicationConfiguration) -> str:
"""Build a json schema from the settings using the stub schema.

:param settings: The application settings
:returns: The json schema
"""
for entry in settings.entries:
subschema: Dict = PARTIAL_SCHEMA["properties"]
dot_parts = entry.settings_file_path(prefix=settings.application_name_dashed).split(".")
for part in dot_parts[:-1]:
if isinstance(subschema, dict):
subschema = subschema.get(part, {}).get("properties")
subschema[dot_parts[-1]]["description"] = entry.short_description
if entry.choices:
subschema[dot_parts[-1]]["enum"] = entry.choices
if entry.value.default is not Constants.NOT_SET:
subschema[dot_parts[-1]]["default"] = entry.value.default
return serialize(
content=PARTIAL_SCHEMA,
content_view=ContentView.NORMAL,
serialization_format=SerializationFormat.JSON,
)
Loading