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

feat(scripts): add field deprecation and doc hyperlink parsing in the script #150

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion build_requirements.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

sys.path.append('./')
sys.path.append("./")
from custom_conf import *

# The file contains helper functions and the mechanism to build the
Expand Down
4 changes: 4 additions & 0 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,7 @@
if 'github_issues' in html_context and html_context['github_issues'] and not disable_feedback_button:
html_js_files.append('github_issue_links.js')
html_js_files.extend(custom_html_js_files)

# Anbox specific function to generate dynamic AMS configuration
# Add this change to conf.py every time the starter pack is upgraded to a later version.
generate_ams_configuration()
17 changes: 15 additions & 2 deletions custom_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,20 @@
## Add any configuration that is not covered by the common conf.py file.

# Define a :center: role that can be used to center the content of table cells.
rst_prolog = '''
rst_prolog = """
.. role:: center
:class: align-center
'''
"""


## Generate dynamic configuration using scripts
# Inject AMS configuration valuues and Node configuration values from the swagger
# specification hosted on Github.
def generate_ams_configuration():
from scripts.ams_configuration import get_swagger_from_url, parse_swagger

with open("scripts/requirements.txt", "r") as f:
for req in f.readlines():
custom_required_modules.append(req)
ams_configuration_file = "reference/ams-configuration.md"
parse_swagger(get_swagger_from_url(), ams_configuration_file)
45 changes: 45 additions & 0 deletions scripts/ams-configuration.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(ref-ams-configuration)=
# AMS configuration

The Anbox Management Service (AMS) provides various configuration items to customise its behaviour. The following table lists the available configuration items and their meaning.

| Name<br/>*(Type, Default)* | Description |
|-----|-----------------------|
{%- for key, config in configs.items() %}
| `{{ key }}`<br/>*({{ config.type }}, {{ config.default or "N/A"}})*{% if config.x_deprecated_since %}<br/>*(Deprecated in {{ config.x_deprecated_since }})*{% endif %} | {{ config.description }}{% if config.x_docs_ref %} See {ref}`{{ config.x_docs_ref }}`. {% endif %}|
{%- endfor %}

(sec-node-configuration)=
## Node-specific configuration

In a cluster setup, there are configuration items that can be customised for each node. The following table lists the available configuration items and their meaning.

| Name<br/>*(Type, Default)* | Description |
|--------------------------|-------------------------|
{%- for key, node in nodes.items() %}
| `{{ key }}`<br/>*({{ node.type }}, {{ node.default or "N/A"}})*{% if node.x_deprecated_since %}<br/>*(Deprecated in {{ node.x_deprecated_since }})*{% endif %} | {{ node.description }} {% if node.x_docs_ref %} See {ref}`{{ node.x_docs_ref }}`. {% endif %}|
{%- endfor %}

See {ref}`howto-configure-cluster-nodes` for instructions on how to set these configuration items.

## Objects managed by AMS

AMS manages various objects such as applications, images, instances, nodes and addons.

The object names must adhere to the following criteria:

* Minimum character limit: 3
* Maximum character limit: 255
* Can contain:
- Alphabets (a-z, A-Z)
- Numbers (0-9)
- Allowed special characters: `-` (hyphen), `_` (underscore), `:` (colon), `.` (period).

When you create an instance, the same criteria apply to the following options as well:

* `boot_activity`
* `platform`
* `boot_package`

The object ids are generated by AMS and have a length of 20 characters.

113 changes: 113 additions & 0 deletions scripts/ams_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env python3

import argparse
import json
from typing import Dict

import requests
from jinja2 import Environment
from jinja2.loaders import FileSystemLoader

SWAGGER_URL = (
"https://canonical.github.io/anbox-cloud.github.com/latest/ams/swagger.json"
)


def main() -> int:
parser = argparse.ArgumentParser(
prog="AMS swagger parser",
description="Extracts AMS configuration data",
)
parser.add_argument(
"-i",
"--input-file",
dest="swagger_path",
required=False,
help="Path to the swagger json file (Should conform to swagger spec 2.0)",
)
parser.add_argument(
"-o",
"--output",
dest="output_file",
help="Destination of the rendered configuration file",
default="reference/ams-configuration.md",
)
args = parser.parse_args()
if args.swagger_path:
with open(args.swagger_path, mode="r") as f:
swagger = json.load(f)
else:
swagger = get_swagger_from_url()
parse_swagger(swagger, args.output_file)


def get_swagger_from_url() -> Dict:
response = requests.get(SWAGGER_URL)
return response.json()


def insert_custom_fields(props: Dict):
for prop in props.values():
val = prop.pop("x-docs-ref", "")
if val:
prop["x_docs_ref"] = val
val = prop.pop("x-deprecated-since", "")
if val:
prop["x_deprecated_since"] = val


def parse_swagger(swagger, output_file):
configs = _parse_config_schema(swagger)
nodes = _parse_node_schema(swagger)
insert_custom_fields(configs)
insert_custom_fields(nodes)
env = Environment(loader=FileSystemLoader("."))
templ = env.get_template("scripts/ams-configuration.md.j2")
text = templ.render(configs=configs, nodes=nodes)
with open(output_file, mode="w+") as op:
op.write(text)


def _parse_config_schema(swagger) -> dict:
ams_config_path = "/1.0/config"
config_props = swagger["paths"][ams_config_path]["get"]["responses"]["200"][
"schema"
]["properties"]["metadata"]["properties"]["config"]["properties"]
for prop in config_props.values():
if "description" in prop:
prop["description"] = prop["description"].replace("\n", " ")
return config_props


def _parse_node_schema(swagger) -> dict:
node_props = swagger["definitions"]["NodePatch"]["properties"]
for base, prop in dict(node_props).items():
key = ""
if prop["type"] == "object":
key = "additionalProperties"
if prop["type"] == "array":
key = "items"
if key:
if schema_name := prop[key].get("$ref"):
name = schema_name.split("/")[-1]
if schema := swagger["definitions"][name]:
for subprop_name, subprop_attrs in schema["properties"].items():
if subprop_name == "id":
continue
new_base = base
if base == "gpus":
new_base = f"{base}.<id>"
node_props[f"{new_base}.{subprop_name.replace('_', '-')}"] = (
subprop_attrs
)
node_props.pop(base)
else:
val = node_props.pop(base)
node_props[base.replace("_", "-")] = val
if "description" in prop:
prop["description"] = prop["description"].replace("\n", " ")
return node_props


if __name__ == "__main__":
SystemExit(main())
7 changes: 7 additions & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
certifi==2024.8.30
charset-normalizer==3.3.2
idna==3.8
Jinja2==3.1.4
MarkupSafe==2.1.5
requests==2.32.3
urllib3==2.2.2
Loading