Skip to content

Commit dc7261d

Browse files
authored
Merge pull request #11 from lilatomic/feature/crd-visualiser
Add a visualiser for Kubernetes CRDs
2 parents e99dd85 + d112173 commit dc7261d

28 files changed

+4169
-1484
lines changed

.github/workflows/pants.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ jobs:
2020
uses: actions/setup-python@v4
2121
with:
2222
python-version: "3.10"
23+
- name: Setup Python
24+
uses: actions/setup-python@v4
25+
with:
26+
python-version: "3.11"
2327
- name: Setup Python
2428
uses: actions/setup-python@v4
2529
with:

alpacloud/argocdkit/Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,3 @@ FROM python:3.13-slim
2727
COPY --from=helm-installer /helm /usr/local/bin/helm
2828
COPY alpacloud.argocdkit/__main__.pex /bin/alpacloud-argocdkit
2929
RUN /bin/alpacloud-argocdkit gen-cfg
30-
31-
COPY alpacloud.argocdkit.test_resources/my_postrenderer.pex /bin/my_postrenderer

alpacloud/argocdkit/test_resources/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ files(
1010
name="k8s_objs",
1111
sources=["**/*.yml", "**/*.yaml", "**/*.tpl", "**/*.json"],
1212
)
13+
14+
docker_image(
15+
name="docker",
16+
repository="cmp",
17+
image_tags=["latesttest"],
18+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM ghcr.io/lilatomic/alpacloud/cmp:0.0.1
2+
3+
COPY alpacloud.argocdkit.test_resources/my_postrenderer.pex /bin/my_postrenderer

alpacloud/argocdkit/test_resources/integration/argocd-repo-server-deployment-patch.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ spec:
99
- name: my-cmp
1010
command: [/var/run/argocd/argocd-cmp-server]
1111
args: [--loglevel, debug]
12-
image: cmp:0.0.1
12+
image: cmp:latesttest
1313
securityContext:
1414
runAsNonRoot: true
1515
runAsUser: 999
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
replicaCount: 3
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: argoproj.io/v1alpha1
2+
kind: Application
3+
metadata:
4+
name: guestbook
5+
namespace: argocd
6+
spec:
7+
destination:
8+
namespace: aa-tgt
9+
server: https://kubernetes.default.svc
10+
project: default
11+
sources:
12+
- path: helm-guestbook
13+
plugin:
14+
env:
15+
- name: foo
16+
value: bar
17+
name: helm-and-python-0.0.1
18+
valueFiles:
19+
- $values/alpacloud/argocdkit/test_resources/integration/my-values.yaml
20+
parameters:
21+
- array:
22+
- /bin/my_postrenderer
23+
name: postRenderers
24+
repoURL: https://github.com/argoproj/argocd-example-apps.git
25+
- repoURL: https://github.com/lilatomic/alpacloud.git
26+
targetRevision: prod
27+
ref: values

alpacloud/crdvis/BUILD

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
python_sources()
2+
3+
python_tests(
4+
name="tests",
5+
dependencies=["./test_resources:k8s_objs", "//:reqs1#pytest-asyncio"],
6+
)
7+
8+
python_distribution(
9+
name="alpacloud.crdvis",
10+
repositories=["@alpacloud.crdvis"],
11+
dependencies=[":crdvis"],
12+
long_description_path="alpacloud/crdvis/readme.md",
13+
provides=python_artifact(
14+
name="alpacloud.crdvis",
15+
version="0.1.0",
16+
description="A visualiser for Kubernetes CRDs",
17+
author="Daniel Goldman",
18+
entry_points={"console_scripts": ["crdvis=alpacloud.crdvis:run_crdvis"]},
19+
classifiers=[
20+
"Development Status :: 3 - Alpha",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
"Programming Language :: Python :: 3.13",
25+
"Topic :: Utilities",
26+
"Topic :: System :: Systems Administration",
27+
],
28+
license="Round Robin 2.0.0",
29+
long_description_content_type="text/markdown",
30+
),
31+
)

alpacloud/crdvis/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
CRDVis package provides visualization tools for Kubernetes CRD resources.
3+
"""
4+
5+
__version__ = "0.1.0"
6+
7+
from . import models, vis
8+
from .vis import main as run_crdvis
9+
10+
__all__ = ["models", "vis", "run_crdvis"]

alpacloud/crdvis/crd.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from __future__ import annotations
2+
3+
import os
4+
import shutil
5+
import subprocess
6+
from urllib.parse import urlparse
7+
8+
import requests
9+
import yaml
10+
11+
from alpacloud.crdvis.models import CustomResourceDefinition
12+
13+
14+
class CRDReadError(Exception):
15+
"""Exception raised when there is an error reading a CRD."""
16+
17+
pass
18+
19+
20+
def read_path(path: str) -> CustomResourceDefinition:
21+
"""
22+
Read a path-like object to fetch a CRD.
23+
24+
Raises:
25+
CRDReadError: If there is an error reading the CRD.
26+
"""
27+
if path.startswith("http://") or path.startswith("https://"):
28+
req = requests.Request("GET", path)
29+
30+
url = urlparse(req.url)
31+
if url.netloc == "github.com":
32+
req.params["raw"] = "true"
33+
34+
response = requests.Session().send(req.prepare(), timeout=30)
35+
36+
if not response.ok:
37+
raise CRDReadError(f"Failed to fetch CRD from {path}: {response.status_code}")
38+
content = response.text
39+
elif path.startswith("file://") or os.path.exists(path):
40+
disk_path = path.rsplit("://", 1)[-1]
41+
if not os.path.exists(disk_path):
42+
raise CRDReadError(f"File not found: {disk_path}")
43+
44+
with open(disk_path, "r", encoding="utf-8") as f:
45+
content = f.read()
46+
47+
elif path.startswith("kubectl://"):
48+
kubectl_crd = path.rsplit("://", 1)[-1]
49+
kubectl_exe = shutil.which("kubectl")
50+
if not kubectl_exe:
51+
raise CRDReadError("kubectl is not installed.")
52+
53+
try:
54+
content = subprocess.check_output([kubectl_exe, "get", "-o", "yaml", "crd", kubectl_crd], timeout=30).decode("utf-8")
55+
except subprocess.SubprocessError as e:
56+
try:
57+
crds = subprocess.check_output([kubectl_exe, "get", "crd"], timeout=30)
58+
if crds:
59+
error_msg = f"crd is not available in cluster {kubectl_crd}"
60+
else:
61+
error_msg = f"Failed to fetch CRDs {kubectl_crd}: {e}"
62+
63+
except subprocess.SubprocessError as e:
64+
error_msg = f"Failed to fetch CRDs {kubectl_crd}: {e}"
65+
raise CRDReadError(error_msg)
66+
else:
67+
content = path
68+
69+
if not content:
70+
raise CRDReadError("Empty content")
71+
doc = yaml.safe_load(content)
72+
return CustomResourceDefinition.model_validate(doc)

0 commit comments

Comments
 (0)