Skip to content

Commit 6f2cd1b

Browse files
authored
Merge pull request timfeirg#37 from kaiix/dev-4721-native-pydantic-refactor
2 parents e408f43 + 673cd59 commit 6f2cd1b

17 files changed

Lines changed: 814 additions & 627 deletions

lain_cli/aliyun.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ def __init__(
2323
) -> None:
2424
if not all([access_key_id, access_key_secret, registry]):
2525
cc = tell_cluster_config()
26-
access_key_id = cc.get("access_key_id")
27-
access_key_secret = cc.get("access_key_secret")
26+
access_key_id = (
27+
getattr(cc, "access_key_id", None) if cc is not None else None
28+
)
29+
access_key_secret = (
30+
getattr(cc, "access_key_secret", None) if cc is not None else None
31+
)
2832
if not all([access_key_id, access_key_secret]):
2933
raise ValueError(
3034
"access_key_id, access_key_secret not provided in cluster config"
3135
)
3236
if not registry:
33-
registry = cc["registry"]
37+
registry = getattr(cc, "registry") if cc is not None else None
3438

3539
assert registry is not None
3640
_, region_id, _, _, repo_namespace = re.split(r"[\./]", registry)

lain_cli/harbor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ def __init__(
1919
) -> None:
2020
if not all([registry, harbor_token]):
2121
cc = tell_cluster_config()
22-
registry = cc["registry"]
23-
if "harbor_token" not in cc:
22+
registry = getattr(cc, "registry") if cc is not None else None
23+
harbor_token = getattr(cc, "harbor_token", None) if cc is not None else None
24+
if not harbor_token:
2425
raise ValueError("harbor_token not provided in cluster config")
25-
harbor_token = cc["harbor_token"]
2626

2727
assert registry is not None
2828
assert harbor_token is not None

lain_cli/kibana.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Kibana(RequestClientMixin):
1515

1616
def __init__(self) -> None:
1717
cc = tell_cluster_config()
18-
kibana_host = cc.get("kibana")
18+
kibana_host = getattr(cc, "kibana", None) if cc is not None else None
1919
if not kibana_host:
2020
error("kibana not configured for this cluster", exit=1)
2121

lain_cli/lain.py

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
pod_text,
3939
top_text,
4040
)
41+
from lain_cli.schemas import ClusterConfigSchema
4142
from lain_cli.scm import tell_scm
4243
from lain_cli.tencent import TencentClient
4344
from lain_cli.utils import (
@@ -52,7 +53,6 @@
5253
HELM_STUCK_STATE,
5354
KUBECONFIG_DIR,
5455
RECENT_TAGS_COUNT,
55-
ClusterConfigSchema,
5656
KVPairType,
5757
banyun,
5858
brief,
@@ -388,7 +388,7 @@ def admin_status(ctx, simple):
388388
).stdout
389389
).splitlines()
390390
cc = tell_cluster_config()
391-
ingress_external_port = cc.get("ingress_external_port", 80)
391+
ingress_external_port = getattr(cc, "ingress_external_port", 80) if cc else 80
392392
urls = []
393393
for ing in ing_list:
394394
host, paths = ing.split()
@@ -519,8 +519,8 @@ def list_waste():
519519
@click.pass_context
520520
def migrate_registry(ctx: click.Context, cc_path: str) -> None:
521521
data = yalo(cc_path)
522-
cc = ClusterConfigSchema.load(data, context={"is_current": True})
523-
registry_addr = cc["registry"]
522+
cc = ClusterConfigSchema.model_validate(data, context={"is_current": True})
523+
registry_addr = getattr(cc, "registry")
524524
dest_registry = tell_registry_client(cc)
525525
if not dest_registry:
526526
error("destination registry client not available", exit=True)
@@ -679,14 +679,19 @@ def lint(ctx, simple):
679679
)
680680

681681
# validate builds config
682-
values = ctx.obj.get("values", {})
682+
values = ctx.obj.get("values")
683683
builds = tell_builds()
684684
if builds:
685685
build_names = set(builds.keys())
686686
# check build references across all workload types
687687
for section in ("deployments", "cronjobs", "statefulSets", "jobs"):
688-
for name, workload in values.get(section, {}).items():
689-
ref = workload.get("build")
688+
workloads = getattr(values, section, {}) or {}
689+
for name, workload in workloads.items():
690+
ref = (
691+
workload.get("build")
692+
if isinstance(workload, dict)
693+
else getattr(workload, "build", None)
694+
)
690695
if ref and ref not in build_names:
691696
error(
692697
f"{section[:-1]} {name} references build '{ref}' which is not defined in builds",
@@ -733,12 +738,24 @@ def lint(ctx, simple):
733738
# warn if multi-build but no default and some workloads lack build field
734739
if len(builds) > 1 and DEFAULT_BUILD_NAME not in build_names:
735740
for section in ("deployments", "cronjobs", "statefulSets", "jobs"):
736-
for proc_name, proc in values.get(section, {}).items():
737-
if (
738-
not proc.get("build")
739-
and not proc.get("image")
740-
and not proc.get("imageTag")
741-
):
741+
workloads = getattr(values, section, {}) or {}
742+
for proc_name, proc in workloads.items():
743+
build_ref = (
744+
proc.get("build")
745+
if isinstance(proc, dict)
746+
else getattr(proc, "build", None)
747+
)
748+
image_ref = (
749+
proc.get("image")
750+
if isinstance(proc, dict)
751+
else getattr(proc, "image", None)
752+
)
753+
image_tag_ref = (
754+
proc.get("imageTag")
755+
if isinstance(proc, dict)
756+
else getattr(proc, "imageTag", None)
757+
)
758+
if not build_ref and not image_ref and not image_tag_ref:
742759
warn(
743760
f"{section[:-1]} {proc_name} has no 'build' field and no 'default' build exists; it will use chart.image"
744761
)
@@ -977,12 +994,12 @@ def logs(
977994
raise BadParameter("cannot use --stern with --kibana")
978995

979996
release_name = tell_release_name()
980-
values = ctx.obj.get("values", {})
997+
values = ctx.obj.get("values")
981998
proc_name = proc[0] if proc else None
982999
selector = None
983-
deploy_names = set(values.get("deployments") or [])
984-
cronjob_names = set(values.get("cronjobs") or [])
985-
job_names = set(values.get("jobs") or [])
1000+
deploy_names = set(values.deployments or []) if values else set()
1001+
cronjob_names = set(values.cronjobs or []) if values else set()
1002+
job_names = set(values.jobs or []) if values else set()
9861003

9871004
if proc_name in deploy_names:
9881005
selector = f"app.kubernetes.io/instance={release_name}-{proc_name}"
@@ -1235,7 +1252,7 @@ def run_job_command(
12351252
# 如果没有在任何 app 内运行 lain job, 则会用 lain 镜像启动一个容器
12361253
ctx.obj["image"] = make_image_str(appname="lain", image_tag="latest")
12371254
cc = tell_cluster_config()
1238-
jfs_mount_path = cc.get("jfs", "")
1255+
jfs_mount_path = getattr(cc, "jfs", "") if cc else ""
12391256
if jfs_mount_path:
12401257
ctx.obj["volumeMounts"] = [{"name": "jfs", "mountPath": jfs_mount_path}]
12411258
ctx.obj["volumes"] = [
@@ -1251,7 +1268,7 @@ def run_job_command(
12511268
# 如果发现是在 lain app 目录内运行 lain job, 就选取一个 deploy,
12521269
# 拿出各种 spec 里的信息,来渲染 job.yaml
12531270
if not deploy:
1254-
deploys = ctx.obj["values"]["deployments"]
1271+
deploys = ctx.obj["values"].deployments
12551272
deploy = list(deploys.keys())[0]
12561273
res = kubectl(
12571274
"get", "deploy", f"{appname}-{deploy}", "-ojson", capture_output=True
@@ -1480,7 +1497,7 @@ def x(ctx: click.Context, deploy_and_command: tuple[str, ...]) -> None:
14801497
# use -- to avoid click confusion on cli options
14811498
lain x -- python3 manage.py foo --bar
14821499
"""
1483-
deploy_names = set(ctx.obj["values"]["deployments"])
1500+
deploy_names = set(ctx.obj["values"].deployments)
14841501
if deploy_and_command:
14851502
deploy, *cmd = deploy_and_command
14861503
if deploy not in deploy_names:
@@ -1546,8 +1563,8 @@ def use(
15461563

15471564
def tell_cluster_line(c, is_current=False):
15481565
prechar = "*" if is_current else " "
1549-
cc = CLUSTERS.get(c) or {}
1550-
extra_docs = cc.get("extra_docs") or ""
1566+
cc = CLUSTERS.get(c)
1567+
extra_docs = cc.extra_docs if cc else ""
15511568
if extra_docs:
15521569
return f"{prechar} {c}, {extra_docs}"
15531570
return f"{prechar} {c}"
@@ -1592,7 +1609,7 @@ def print_cluster_and_exit(cluster=None):
15921609
os.symlink(src, dest)
15931610
cc = tell_cluster_config(cluster_name)
15941611
if set_context:
1595-
ns = cc.get("namespace", "default")
1612+
ns = getattr(cc, "namespace", "default") if cc else "default"
15961613
kubectl(
15971614
"config",
15981615
"set-context",
@@ -1603,7 +1620,7 @@ def print_cluster_and_exit(cluster=None):
16031620
else:
16041621
kubectl_version_challenge(check=False)
16051622

1606-
if turn and cc.get("instance_ids"):
1623+
if turn and cc and getattr(cc, "instance_ids", None):
16071624
echo("wait for cluster up...")
16081625
lain_("admin", "turn", "on", exit=True)
16091626

@@ -1694,7 +1711,7 @@ def restart(ctx, procs_or_release_name, selectors, wait, graceful):
16941711
def update_image(ctx: click.Context, procs: tuple[str, ...], deduce: bool) -> None:
16951712
"""update, and only update image for some proc"""
16961713
values = ctx.obj["values"]
1697-
choices = set(values["procs"].keys())
1714+
choices = set(values.procs.keys())
16981715
if not procs:
16991716
error(f"specify at least one proc, choose from: {choices}", exit=1)
17001717

@@ -1723,7 +1740,7 @@ def update_image(ctx: click.Context, procs: tuple[str, ...], deduce: bool) -> No
17231740

17241741
image = registry.make_image(image_tag)
17251742
for proc in selected_procs:
1726-
resource_type = "deployment" if proc in values["deployments"] else "cronjob"
1743+
resource_type = "deployment" if proc in values.deployments else "cronjob"
17271744
res = kubectl(
17281745
"set",
17291746
"image",
@@ -1992,7 +2009,7 @@ def deploy(ctx, pairs, delete_after, build, canary, wait):
19922009
if appname != tell_release_name():
19932010
error("do not use canary deploy while values are being overridden", exit=1)
19942011

1995-
ctx.obj["values"]["releaseName"] = canary_name
2012+
ctx.obj["values"].releaseName = canary_name
19962013
elif helm_status(canary_name):
19972014
error("cannot proceed due to on-going canary deploy", exit=1)
19982015

@@ -2060,7 +2077,7 @@ def deploy(ctx, pairs, delete_after, build, canary, wait):
20602077
if age > deploy_duration:
20612078
re_creation_headsup = True
20622079

2063-
tests = ctx.obj["values"].get("tests")
2080+
tests = ctx.obj["values"].tests
20642081
if tests:
20652082
lain_("wait")
20662083
# sometimes test pods are cleaned up prematurely, and this command will fail
@@ -2240,7 +2257,9 @@ def delete(
22402257
release_name = app_name
22412258
ctx.obj["appname"] = app_name
22422259

2243-
persistentVolumeClaims = ctx.obj["values"].get("persistentVolumeClaims") or {}
2260+
persistentVolumeClaims = (
2261+
getattr(ctx.obj["values"], "persistentVolumeClaims", None) or {}
2262+
)
22442263
for pvc in persistentVolumeClaims.values():
22452264
sc_name = pvc["storageClassName"]
22462265
if not storage_class_can_reattach(sc_name) and not force:
@@ -2417,14 +2436,24 @@ def run(
24172436
raise BadParameter("cannot use --proc with --prepare")
24182437
image: str
24192438
if proc_name:
2420-
procs = ctx.obj["values"]["procs"]
2439+
procs = ctx.obj["values"].procs
24212440
proc = procs[proc_name]
2422-
try:
2423-
image = proc["image"]
2424-
except KeyError:
2425-
image_tag = proc["imageTag"]
2441+
image_name = (
2442+
proc.get("image")
2443+
if isinstance(proc, dict)
2444+
else getattr(proc, "image", None)
2445+
)
2446+
if image_name is not None:
2447+
image = image_name
2448+
else:
2449+
image_tag = (
2450+
proc.get("imageTag")
2451+
if isinstance(proc, dict)
2452+
else getattr(proc, "imageTag", None)
2453+
)
2454+
assert image_tag is not None
24262455
image = make_image_str(image_tag=image_tag)
2427-
if prepare:
2456+
elif prepare:
24282457
image = make_image_str(image_tag="prepare")
24292458
else:
24302459
meta = lain_meta()
@@ -2555,7 +2584,7 @@ def push(ctx, images, pull, overwrite_latest, registry, build_name):
25552584
"""
25562585
if not registry:
25572586
cluster = ctx.obj["cluster"]
2558-
registry = CLUSTERS[cluster]["registry"]
2587+
registry = CLUSTERS[cluster].registry
25592588

25602589
if images:
25612590
for image in images:
@@ -2893,7 +2922,7 @@ def version(ctx, images_count):
28932922
def image(ctx):
28942923
tag = lain_meta()
28952924
cc = tell_cluster_config()
2896-
registry_addr = cc["registry"]
2925+
registry_addr = getattr(cc, "registry")
28972926
appname = ctx.obj["appname"]
28982927
image_tag = f"{registry_addr}/{appname}:{tag}"
28992928
echo(image_tag)

lain_cli/prometheus.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@ class Prometheus(RequestClientMixin):
2828
def __init__(self, endpoint: str | None = None) -> None:
2929
if not endpoint:
3030
cc = tell_cluster_config()
31-
endpoint = cc.get("prometheus")
31+
endpoint = getattr(cc, "prometheus", None) if cc is not None else None
3232
if not endpoint:
3333
raise click.Abort(f"prometheus not provided in cluster config: {cc}")
3434

3535
ctx = context(silent=True)
36-
self.query_range = (
37-
ctx.obj.get("values", {}).get("prometheus_query_range", "7d")
38-
if ctx
39-
else "7d"
40-
)
36+
if ctx:
37+
values = ctx.obj.get("values")
38+
if isinstance(values, dict):
39+
self.query_range = values.get("prometheus_query_range", "7d")
40+
else:
41+
self.query_range = getattr(values, "prometheus_query_range", "7d")
42+
else:
43+
self.query_range = "7d"
4144
self.query_step = int(int(parse_timespan(self.query_range)) / 1440)
4245
self.endpoint = endpoint
4346

@@ -51,7 +54,7 @@ def query_cpu(
5154
self, appname: str, proc_name: str, **kwargs: Any
5255
) -> PrometheusResult:
5356
cc = tell_cluster_config()
54-
query_template = cc.get("pql_template", {}).get("cpu")
57+
query_template = (getattr(cc, "pql_template", None) or {}).get("cpu")
5558
if not query_template:
5659
raise ValueError("pql_template.cpu not configured in cluster config")
5760
q = query_template.format(
@@ -85,7 +88,9 @@ def memory_quantile(
8588
self, appname: str, proc_name: str, **kwargs: Any
8689
) -> int | None:
8790
cc = tell_cluster_config()
88-
query_template = cc.get("pql_template", {}).get("memory_quantile")
91+
query_template = (getattr(cc, "pql_template", None) or {}).get(
92+
"memory_quantile"
93+
)
8994
if not query_template:
9095
raise ValueError(
9196
"pql_template.memory_quantile not configured in cluster config"
@@ -163,7 +168,7 @@ class Alertmanager(RequestClientMixin):
163168
def __init__(self, endpoint: str | None = None) -> None:
164169
if not endpoint:
165170
cc = tell_cluster_config()
166-
endpoint = cc.get("alertmanager")
171+
endpoint = getattr(cc, "alertmanager", None) if cc is not None else None
167172
if not endpoint:
168173
raise click.Abort(f"alertmanager not provided in cluster config: {cc}")
169174

lain_cli/registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Registry(RequestClientMixin, RegistryUtils):
1818
def __init__(self, registry: str | None = None, **kwargs: Any) -> None:
1919
if not registry:
2020
cc = tell_cluster_config()
21-
registry = cc["registry"]
21+
registry = getattr(cc, "registry") if cc is not None else None
2222

2323
assert registry is not None
2424
self.registry = registry

0 commit comments

Comments
 (0)