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

class-generator: fix when kind do not have properties #2181

Merged
merged 20 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
747b5a5
Do not fail when kind do not have properties
myakove Oct 27, 2024
6739538
Do not fail when kind do not have properties
myakove Oct 27, 2024
f7b2bc1
Do not fail when kind do not have properties
myakove Oct 27, 2024
dcd6139
add class_generator/__cluster_version__.txt file
myakove Oct 27, 2024
43025c7
update schema
myakove Oct 27, 2024
eaf3d68
Do not update existing kind in __resources-mappings.json if cluster v…
myakove Oct 27, 2024
4babbb3
Do not update existing kind in __resources-mappings.json if cluster v…
myakove Oct 27, 2024
a9191a5
Do not update existing kind in __resources-mappings.json if cluster v…
myakove Oct 27, 2024
21c7bd7
Do not update existing kind in __resources-mappings.json if cluster v…
myakove Oct 27, 2024
0d4c1d7
Fix type
myakove Oct 27, 2024
41a4e87
Use the right generated definition file
myakove Oct 27, 2024
e8eba86
Add .coderabbit.yaml
myakove Oct 27, 2024
1100129
add type for schema_definition_file
myakove Oct 27, 2024
bfa7f58
Use Path(gettempdir()) for tmp dir
myakove Oct 27, 2024
47fc7fa
exit if cluster version file is missing
myakove Oct 27, 2024
3a7a53c
exit if error in copy files
myakove Oct 27, 2024
17d9eba
Merge branch 'main' of github.com:RedHatQE/openshift-python-wrapper i…
myakove Oct 28, 2024
bdd1365
Merge branch 'main' of github.com:RedHatQE/openshift-python-wrapper i…
myakove Oct 28, 2024
2a2a9e2
Merge branch 'main' of github.com:RedHatQE/openshift-python-wrapper i…
myakove Oct 29, 2024
ba44337
Merge branch 'main' of github.com:RedHatQE/openshift-python-wrapper i…
myakove Oct 31, 2024
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
92 changes: 92 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
language: en-US
tone_instructions: ""
early_access: false
enable_free_tier: true
reviews:
profile: chill
request_changes_workflow: false
high_level_summary: true
high_level_summary_placeholder: "@coderabbitai summary"
auto_title_placeholder: "@coderabbitai"
review_status: true
commit_status: true
poem: false
collapse_walkthrough: false
sequence_diagrams: false
changed_files_summary: true
labeling_instructions: []
path_filters: ["!class_generator/schema/**"]
path_instructions: []
abort_on_close: true
auto_review:
enabled: true
auto_incremental_review: true
ignore_title_keywords: []
labels: []
drafts: false
base_branches: []
tools:
shellcheck:
enabled: true
ruff:
enabled: true
markdownlint:
enabled: true
github-checks:
enabled: true
timeout_ms: 90000
languagetool:
enabled: true
enabled_only: false
level: default
biome:
enabled: true
hadolint:
enabled: true
swiftlint:
enabled: true
phpstan:
enabled: true
level: default
golangci-lint:
enabled: true
yamllint:
enabled: true
gitleaks:
enabled: true
checkov:
enabled: true
detekt:
enabled: true
eslint:
enabled: true
rubocop:
enabled: true
buf:
enabled: true
regal:
enabled: true
actionlint:
enabled: true
pmd:
enabled: true
cppcheck:
enabled: true
semgrep:
enabled: true
circleci:
enabled: true
chat:
auto_reply: true
knowledge_base:
opt_out: false
learnings:
scope: auto
issues:
scope: auto
jira:
project_keys: []
linear:
team_keys: []
pull_requests:
scope: auto
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ venv/
ENV/
env.bak/
venv.bak/
.envrc

# Spyder project settings
.spyderproject
Expand Down Expand Up @@ -123,6 +124,3 @@ local-cluster/_hco/
node_modules/
package.json
package-lock.json

# class generator script
class_generator/__ocp-openapi.json
1 change: 1 addition & 0 deletions class_generator/__cluster_version__.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1.31.0
1 change: 0 additions & 1 deletion class_generator/__k8s-openapi-v1.29.8__.json

This file was deleted.

97 changes: 75 additions & 22 deletions class_generator/class_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import sys
import requests
from pathlib import Path
from packaging.version import Version
import shutil
from tempfile import gettempdir

import textwrap
from typing import Any, Dict, List, Tuple
Expand All @@ -31,37 +34,33 @@
LOGGER = get_logger(name="class_generator")
TESTS_MANIFESTS_DIR: str = "class_generator/tests/manifests"
SCHEMA_DIR: str = "class_generator/schema"
SCHEMA_DEFINITION_FILE: str = os.path.join(SCHEMA_DIR, "_definitions.json")
RESOURCES_MAPPING_FILE: str = os.path.join(SCHEMA_DIR, "__resources-mappings.json")
MISSING_DESCRIPTION_STR: str = "No field description from API; please add description"


def _is_kind_and_namespaced(client: str, _key: str, _data: Dict[str, Any]) -> Dict[str, Any]:
x_kubernetes_group_version_kind = extract_group_kind_version(_kind_schema=_data)
_kind = x_kubernetes_group_version_kind["kind"]
_group = x_kubernetes_group_version_kind.get("group")
_version = x_kubernetes_group_version_kind.get("version")
_group_and_version = f"{_group}/{_version}" if _group else _version

def _is_kind_and_namespaced(
client: str, _key: str, _data: Dict[str, Any], kind: str, group: str, version: str
) -> Dict[str, Any]:
_group_and_version = f"{group}/{version}" if group else version
not_resource_dict = {"is_kind": False, "kind": _key}

# if explain command failed, this is not a resource
if not run_command(command=shlex.split(f"{client} explain {_kind}"), check=False, log_errors=False)[0]:
if not run_command(command=shlex.split(f"{client} explain {kind}"), check=False, log_errors=False)[0]:
return not_resource_dict

# check if this as a valid version for the resource.
api_resources_base_cmd = f"bash -c '{client} api-resources"

# check if this as a valid version for the resource.
if run_command(
command=shlex.split(f"{api_resources_base_cmd} | grep -w {_kind} | grep {_group_and_version}'"),
command=shlex.split(f"{api_resources_base_cmd} | grep -w {kind} | grep {_group_and_version}'"),
check=False,
log_errors=False,
)[0]:
# Check if the resource if namespaced.
_data["namespaced"] = (
run_command(
command=shlex.split(
f"{api_resources_base_cmd} --namespaced | grep -w {_kind} | grep {_group_and_version} | wc -l'"
f"{api_resources_base_cmd} --namespaced | grep -w {kind} | grep {_group_and_version} | wc -l'"
),
check=False,
log_errors=False,
Expand All @@ -73,7 +72,7 @@ def _is_kind_and_namespaced(client: str, _key: str, _data: Dict[str, Any]) -> Di
return not_resource_dict


def map_kind_to_namespaced(client: str):
def map_kind_to_namespaced(client: str, newer_cluster_version: bool, schema_definition_file: Path) -> None:
not_kind_file: str = os.path.join(SCHEMA_DIR, "__not-kind.txt")

resources_mapping = read_resources_mapping_file()
Expand All @@ -84,20 +83,38 @@ def map_kind_to_namespaced(client: str):
else:
not_kind_list = []

with open(SCHEMA_DEFINITION_FILE) as fd:
with open(schema_definition_file) as fd:
_definitions_json_data = json.load(fd)

_kind_data_futures: List[Future] = []
with ThreadPoolExecutor() as executor:
for _key, _data in _definitions_json_data["definitions"].items():
_group_version_kind = _data.get("x-kubernetes-group-version-kind")
if not _group_version_kind:
if not _data.get("x-kubernetes-group-version-kind"):
continue

if _key in not_kind_list:
continue

_kind_data_futures.append(executor.submit(_is_kind_and_namespaced, client=client, _key=_key, _data=_data))
x_kubernetes_group_version_kind = extract_group_kind_version(_kind_schema=_data)
_kind = x_kubernetes_group_version_kind["kind"]
_group = x_kubernetes_group_version_kind.get("group", "")
_version = x_kubernetes_group_version_kind.get("version", "")

# Do not add the resource if it is already in the mapping and the cluster version is not newer than the last
if resources_mapping.get(_kind.lower()) and not newer_cluster_version:
continue

_kind_data_futures.append(
executor.submit(
_is_kind_and_namespaced,
client=client,
_key=_key,
_data=_data,
kind=_kind,
group=_group,
version=_version,
)
)

_temp_resources_mappings: Dict[Any, Any] = {}
for res in as_completed(_kind_data_futures):
Expand Down Expand Up @@ -175,17 +192,53 @@ def update_kind_schema():
LOGGER.error("Failed to get openapi schema.")
sys.exit(1)

cluster_version_file = Path("class_generator/__cluster_version__.txt")
try:
with open(cluster_version_file, "r") as fd:
last_cluster_version_generated = fd.read().strip()
except (FileNotFoundError, IOError) as exp:
LOGGER.error(f"Failed to read cluster version file: {exp}")
sys.exit(1)

cluster_version = get_server_version(client=client)
cluster_version = cluster_version.split("+")[0]
ocp_openapi_json_file = f"class_generator/__k8s-openapi-{cluster_version}__.json"
ocp_openapi_json_file = Path(gettempdir()) / f"__k8s-openapi-{cluster_version}__.json"
last_cluster_version_generated: str = ""

newer_version: bool = Version(cluster_version) > Version(last_cluster_version_generated)

if newer_version:
with open(cluster_version_file, "w") as fd:
fd.write(cluster_version)

with open(ocp_openapi_json_file, "w") as fd:
fd.write(data.text)

if not run_command(command=shlex.split(f"{openapi2jsonschema_str} {ocp_openapi_json_file} -o {SCHEMA_DIR}"))[0]:
tmp_schema_dir = Path(gettempdir()) / f"{SCHEMA_DIR}-{cluster_version}"

if not run_command(command=shlex.split(f"{openapi2jsonschema_str} {ocp_openapi_json_file} -o {tmp_schema_dir}"))[0]:
LOGGER.error("Failed to generate schema.")
sys.exit(1)

map_kind_to_namespaced(client=client)
if newer_version:
# copy all files from tmp_schema_dir to schema dir
shutil.copytree(src=tmp_schema_dir, dst=SCHEMA_DIR, dirs_exist_ok=True)

else:
# Copy only new files from tmp_schema_dir to schema dir
for root, _, files in os.walk(tmp_schema_dir):
for file_ in files:
dst_file = Path(SCHEMA_DIR) / file_
try:
if not os.path.isfile(dst_file):
shutil.copy(src=Path(root) / file_, dst=dst_file)
except (OSError, IOError) as exp:
LOGGER.error(f"Failed to copy file {file_}: {exp}")
sys.exit(1)

map_kind_to_namespaced(
client=client, newer_cluster_version=newer_version, schema_definition_file=ocp_openapi_json_file
)


def convert_camel_case_to_snake_case(string_: str) -> str:
Expand Down Expand Up @@ -473,7 +526,7 @@ def parse_explain(
"spec": [],
}

schema_properties: Dict[str, Any] = _kind_schema["properties"]
schema_properties: Dict[str, Any] = _kind_schema.get("properties", {})
fields_required = _kind_schema.get("required", [])

resource_dict.update(extract_group_kind_version(_kind_schema=_kind_schema))
Expand Down Expand Up @@ -608,7 +661,7 @@ def write_and_format_rendered(filepath: str, data: str, user_code: str = "") ->

for op in ("format", "check"):
run_command(
command=shlex.split(f"uv run ruff {op} {filepath}"),
command=shlex.split(f"uvx ruff {op} {filepath}"),
verify_stderr=False,
check=False,
)
Expand Down
Loading