@intentius/chant-lexicon-k8s 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/src/codegen/docs.ts +5 -1
- package/src/describe-resources.test.ts +162 -0
- package/src/describe-resources.ts +134 -0
- package/src/index.ts +2 -1
- package/src/plugin.test.ts +2 -2
- package/src/plugin.ts +7 -2
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "26168ff9d5f4d906e451b0e2c9e7a868b9ae3ab366579d9abf76a45ace9a3b95",
|
|
5
5
|
"meta.json": "970c70eb6eea1686f7cb8ce6ceccc46ce2e57d696d344f1dd52460e266f9a56c",
|
|
6
6
|
"types/index.d.ts": "07473e029254345488bc9952585e88bcf18da79d1026cc26744027683f472554",
|
|
7
7
|
"rules/hardcoded-namespace.ts": "ba3f43f2adbffdd87db20a2c45839354ceecda1b9f04f29ae31c4c077dddc7ec",
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"skills/chant-k8s-gke.md": "8938840bf9ef5ed58d6333fdd773b3dd54ecaf25a9df35e58f7f5c3355d4928f",
|
|
43
43
|
"skills/chant-k8s-aks.md": "e18f0e2b055f72cd7a37deaf258d7027c2d4d3e286e8fd4975b27a1f981a3ad9"
|
|
44
44
|
},
|
|
45
|
-
"composite": "
|
|
45
|
+
"composite": "de04872487ff8b14bc40143791702adeea08ff52942eca65e90e6611623ed767"
|
|
46
46
|
}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
package/src/codegen/docs.ts
CHANGED
|
@@ -548,7 +548,11 @@ const { statefulSet, service } = StatefulApp({
|
|
|
548
548
|
slug: "composite-examples",
|
|
549
549
|
title: "Examples: Composites",
|
|
550
550
|
description: "Composite examples — WebApp, CronWorkload, AutoscaledService, WorkerPool, NamespaceEnv, NodeAgent",
|
|
551
|
-
content: `
|
|
551
|
+
content: `import Diagram from '../../components/Diagram.astro';
|
|
552
|
+
|
|
553
|
+
Composites are higher-level constructs that produce multiple coordinated K8s resources from a single function call.
|
|
554
|
+
|
|
555
|
+
<Diagram name="composite-expansion" alt="Single CockroachDbCluster composite declaration expanding into Namespace, StatefulSet, Service, ServiceAccount, NetworkPolicy, PodDisruptionBudget, and ConfigMap resources serialized to one YAML file" caption="Composite expansion: one declaration → many resources" />
|
|
552
556
|
|
|
553
557
|
## WebApp
|
|
554
558
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
const execMock = vi.fn();
|
|
4
|
+
vi.mock("node:child_process", async () => {
|
|
5
|
+
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
|
|
6
|
+
return { ...actual, exec: (cmd: string, cb: (err: Error | null, out: { stdout: string; stderr: string }) => void) => {
|
|
7
|
+
Promise.resolve(execMock(cmd)).then(
|
|
8
|
+
(out) => cb(null, out),
|
|
9
|
+
(err) => cb(err as Error, { stdout: "", stderr: "" }),
|
|
10
|
+
);
|
|
11
|
+
} };
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const { describeResources } = await import("./describe-resources");
|
|
15
|
+
|
|
16
|
+
function makeEntities(records: Array<{ name: string; entityType: string; props: Record<string, unknown> }>) {
|
|
17
|
+
return new Map(records.map((r) => [r.name, { entityType: r.entityType, props: r.props }]));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("k8s describeResources", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
execMock.mockReset();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("queries kubectl for each declared K8s entity and maps response to ResourceMetadata", async () => {
|
|
26
|
+
execMock.mockImplementation((cmd: string) => {
|
|
27
|
+
if (cmd.includes("deployment.apps web")) {
|
|
28
|
+
return {
|
|
29
|
+
stdout: JSON.stringify({
|
|
30
|
+
metadata: { name: "web", namespace: "prod", uid: "uid-1", creationTimestamp: "2026-05-01T00:00:00Z", labels: { app: "web" } },
|
|
31
|
+
status: { readyReplicas: 3, replicas: 3 },
|
|
32
|
+
}),
|
|
33
|
+
stderr: "",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (cmd.includes("service web-svc")) {
|
|
37
|
+
return {
|
|
38
|
+
stdout: JSON.stringify({
|
|
39
|
+
metadata: { name: "web-svc", namespace: "prod", uid: "uid-2", creationTimestamp: "2026-05-01T00:00:00Z" },
|
|
40
|
+
}),
|
|
41
|
+
stderr: "",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`unexpected cmd: ${cmd}`);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const entities = makeEntities([
|
|
48
|
+
{ name: "web", entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web", namespace: "prod" } } },
|
|
49
|
+
{ name: "webSvc", entityType: "K8s::Core::Service", props: { metadata: { name: "web-svc", namespace: "prod" } } },
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["web", "webSvc"], entities });
|
|
53
|
+
|
|
54
|
+
expect(result["web"]).toMatchObject({
|
|
55
|
+
type: "K8s::Apps::Deployment",
|
|
56
|
+
physicalId: "uid-1",
|
|
57
|
+
status: "READY",
|
|
58
|
+
attributes: expect.objectContaining({ namespace: "prod", labels: { app: "web" } }),
|
|
59
|
+
});
|
|
60
|
+
expect(result["webSvc"]).toMatchObject({
|
|
61
|
+
type: "K8s::Core::Service",
|
|
62
|
+
physicalId: "uid-2",
|
|
63
|
+
status: "PRESENT",
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("kubectl-not-found leaves entity out of the result (so state diff reports as missing)", async () => {
|
|
68
|
+
execMock.mockImplementation(() => { throw new Error('Error from server (NotFound): deployments.apps "missing" not found'); });
|
|
69
|
+
|
|
70
|
+
const entities = makeEntities([
|
|
71
|
+
{ name: "missing", entityType: "K8s::Apps::Deployment", props: { metadata: { name: "missing", namespace: "prod" } } },
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["missing"], entities });
|
|
75
|
+
|
|
76
|
+
expect(result).toEqual({});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("entity types without kubectl mapping are warn-skipped", async () => {
|
|
80
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
81
|
+
const entities = makeEntities([
|
|
82
|
+
{ name: "exotic", entityType: "K8s::Custom::CRD::SomeOperator", props: { metadata: { name: "x" } } },
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["exotic"], entities });
|
|
86
|
+
|
|
87
|
+
expect(result).toEqual({});
|
|
88
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("kubectl mapping"));
|
|
89
|
+
expect(warnSpy.mock.calls[0][0]).toContain("K8s::Custom::CRD::SomeOperator");
|
|
90
|
+
warnSpy.mockRestore();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("Deployment with replicas != readyReplicas reports PROGRESSING", async () => {
|
|
94
|
+
execMock.mockResolvedValue({
|
|
95
|
+
stdout: JSON.stringify({
|
|
96
|
+
metadata: { name: "web", namespace: "prod", uid: "uid", creationTimestamp: "t" },
|
|
97
|
+
status: { readyReplicas: 1, replicas: 3 },
|
|
98
|
+
}),
|
|
99
|
+
stderr: "",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const entities = makeEntities([
|
|
103
|
+
{ name: "web", entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web", namespace: "prod" } } },
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["web"], entities });
|
|
107
|
+
|
|
108
|
+
expect(result["web"].status).toBe("PROGRESSING(1/3)");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("Pod uses status.phase as the status", async () => {
|
|
112
|
+
execMock.mockResolvedValue({
|
|
113
|
+
stdout: JSON.stringify({
|
|
114
|
+
metadata: { name: "p", namespace: "prod", uid: "uid", creationTimestamp: "t" },
|
|
115
|
+
status: { phase: "Running" },
|
|
116
|
+
}),
|
|
117
|
+
stderr: "",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const entities = makeEntities([
|
|
121
|
+
{ name: "p", entityType: "K8s::Core::Pod", props: { metadata: { name: "p", namespace: "prod" } } },
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["p"], entities });
|
|
125
|
+
|
|
126
|
+
expect(result["p"].status).toBe("Running");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("namespace-less resource omits the -n flag", async () => {
|
|
130
|
+
let receivedCmd = "";
|
|
131
|
+
execMock.mockImplementation((cmd: string) => {
|
|
132
|
+
receivedCmd = cmd;
|
|
133
|
+
return {
|
|
134
|
+
stdout: JSON.stringify({
|
|
135
|
+
metadata: { name: "mynamespace", uid: "uid", creationTimestamp: "t" },
|
|
136
|
+
status: { phase: "Active" },
|
|
137
|
+
}),
|
|
138
|
+
stderr: "",
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const entities = makeEntities([
|
|
143
|
+
{ name: "ns", entityType: "K8s::Core::Namespace", props: { metadata: { name: "mynamespace" } } },
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
await describeResources({ environment: "prod", buildOutput: "", entityNames: ["ns"], entities });
|
|
147
|
+
|
|
148
|
+
expect(receivedCmd).not.toContain("-n");
|
|
149
|
+
expect(receivedCmd).toContain("namespace mynamespace");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("entity without metadata.name is silently skipped", async () => {
|
|
153
|
+
const entities = makeEntities([
|
|
154
|
+
{ name: "broken", entityType: "K8s::Apps::Deployment", props: {} },
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
const result = await describeResources({ environment: "prod", buildOutput: "", entityNames: ["broken"], entities });
|
|
158
|
+
|
|
159
|
+
expect(result).toEqual({});
|
|
160
|
+
expect(execMock).not.toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live introspection of a Kubernetes cluster — implements the
|
|
3
|
+
* LexiconPlugin.describeResources() contract for the k8s lexicon.
|
|
4
|
+
*
|
|
5
|
+
* For each declared K8s entity, runs `kubectl get <kind> <name> [-n <ns>] -o json`
|
|
6
|
+
* and maps the response to a ResourceMetadata entry keyed by the chant entity
|
|
7
|
+
* name (using the props.metadata.name + props.metadata.namespace from #39's
|
|
8
|
+
* entity-prop pass-through).
|
|
9
|
+
*
|
|
10
|
+
* Resource-not-found is silent — `state diff --live` then reports it as
|
|
11
|
+
* missing (declared, not in cloud). Unknown entity types are warn-skipped;
|
|
12
|
+
* extending the KUBECTL_RESOURCE map covers more.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { exec } from "node:child_process";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
18
|
+
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Map chant entity types to `kubectl get` resource names. Add entries here
|
|
23
|
+
* as new types are needed.
|
|
24
|
+
*/
|
|
25
|
+
const KUBECTL_RESOURCE: Record<string, string> = {
|
|
26
|
+
"K8s::Apps::Deployment": "deployment.apps",
|
|
27
|
+
"K8s::Apps::StatefulSet": "statefulset.apps",
|
|
28
|
+
"K8s::Apps::DaemonSet": "daemonset.apps",
|
|
29
|
+
"K8s::Apps::ReplicaSet": "replicaset.apps",
|
|
30
|
+
"K8s::Core::Service": "service",
|
|
31
|
+
"K8s::Core::ConfigMap": "configmap",
|
|
32
|
+
"K8s::Core::Secret": "secret",
|
|
33
|
+
"K8s::Core::Namespace": "namespace",
|
|
34
|
+
"K8s::Core::Pod": "pod",
|
|
35
|
+
"K8s::Core::PersistentVolumeClaim": "persistentvolumeclaim",
|
|
36
|
+
"K8s::Core::ServiceAccount": "serviceaccount",
|
|
37
|
+
"K8s::Batch::Job": "job.batch",
|
|
38
|
+
"K8s::Batch::CronJob": "cronjob.batch",
|
|
39
|
+
"K8s::Networking::Ingress": "ingress.networking.k8s.io",
|
|
40
|
+
"K8s::Networking::NetworkPolicy": "networkpolicy.networking.k8s.io",
|
|
41
|
+
"K8s::Rbac::Role": "role.rbac.authorization.k8s.io",
|
|
42
|
+
"K8s::Rbac::RoleBinding": "rolebinding.rbac.authorization.k8s.io",
|
|
43
|
+
"K8s::Rbac::ClusterRole": "clusterrole.rbac.authorization.k8s.io",
|
|
44
|
+
"K8s::Rbac::ClusterRoleBinding": "clusterrolebinding.rbac.authorization.k8s.io",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface KubectlResponse {
|
|
48
|
+
metadata?: {
|
|
49
|
+
name?: string;
|
|
50
|
+
namespace?: string;
|
|
51
|
+
uid?: string;
|
|
52
|
+
creationTimestamp?: string;
|
|
53
|
+
labels?: Record<string, string>;
|
|
54
|
+
resourceVersion?: string;
|
|
55
|
+
};
|
|
56
|
+
status?: {
|
|
57
|
+
phase?: string;
|
|
58
|
+
[k: string]: unknown;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function pruneUndefined<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
|
|
63
|
+
const out: Record<string, unknown> = {};
|
|
64
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
65
|
+
if (v !== undefined) out[k] = v;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function statusFromKubectl(obj: KubectlResponse): string {
|
|
71
|
+
// Different K8s resource types report status differently. Fall back to
|
|
72
|
+
// "PRESENT" if we can't extract a meaningful field.
|
|
73
|
+
const phase = obj.status?.phase;
|
|
74
|
+
if (typeof phase === "string") return phase;
|
|
75
|
+
// Deployment/StatefulSet — readyReplicas == replicas → READY
|
|
76
|
+
const status = obj.status as Record<string, unknown> | undefined;
|
|
77
|
+
if (status && typeof status.readyReplicas === "number" && typeof status.replicas === "number") {
|
|
78
|
+
return status.readyReplicas === status.replicas ? "READY" : `PROGRESSING(${status.readyReplicas}/${status.replicas})`;
|
|
79
|
+
}
|
|
80
|
+
return "PRESENT";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function describeResources(options: {
|
|
84
|
+
environment: string;
|
|
85
|
+
buildOutput: string;
|
|
86
|
+
entityNames: string[];
|
|
87
|
+
entities: Map<string, { entityType: string; props: Record<string, unknown> }>;
|
|
88
|
+
}): Promise<Record<string, ResourceMetadata>> {
|
|
89
|
+
const result: Record<string, ResourceMetadata> = {};
|
|
90
|
+
const skippedTypes = new Set<string>();
|
|
91
|
+
|
|
92
|
+
for (const [entityName, { entityType, props }] of options.entities) {
|
|
93
|
+
const kubectlResource = KUBECTL_RESOURCE[entityType];
|
|
94
|
+
if (!kubectlResource) {
|
|
95
|
+
skippedTypes.add(entityType);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const metadata = props.metadata as { name?: string; namespace?: string } | undefined;
|
|
100
|
+
const name = metadata?.name;
|
|
101
|
+
if (!name) continue;
|
|
102
|
+
|
|
103
|
+
const nsArg = metadata.namespace ? ["-n", metadata.namespace] : [];
|
|
104
|
+
const cmd = ["kubectl", "get", kubectlResource, name, ...nsArg, "-o", "json"].join(" ");
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const { stdout } = await execAsync(cmd);
|
|
108
|
+
const obj: KubectlResponse = JSON.parse(stdout);
|
|
109
|
+
result[entityName] = {
|
|
110
|
+
type: entityType,
|
|
111
|
+
physicalId: obj.metadata?.uid,
|
|
112
|
+
status: statusFromKubectl(obj),
|
|
113
|
+
lastUpdated: obj.metadata?.creationTimestamp,
|
|
114
|
+
attributes: pruneUndefined({
|
|
115
|
+
namespace: obj.metadata?.namespace,
|
|
116
|
+
labels: obj.metadata?.labels,
|
|
117
|
+
resourceVersion: obj.metadata?.resourceVersion,
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
} catch {
|
|
121
|
+
// Resource not found / kubectl error — leave it out so state diff
|
|
122
|
+
// can report it as missing. Don't fail the whole snapshot.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (skippedTypes.size > 0) {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.warn(
|
|
129
|
+
`[k8s] skipped ${skippedTypes.size} entity type(s) without kubectl mapping: ${[...skippedTypes].join(", ")}`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ export {
|
|
|
21
21
|
BatchJob, SecureIngress, ConfiguredApp, SidecarApp, MonitoredService, NetworkIsolatedApp,
|
|
22
22
|
IrsaServiceAccount, AlbIngress, EbsStorageClass, EfsStorageClass, FluentBitAgent, ExternalDnsAgent, AdotCollector,
|
|
23
23
|
MetricsServer, WorkloadIdentityServiceAccount, GcePdStorageClass, FilestoreStorageClass, GkeGateway, ConfigConnectorContext,
|
|
24
|
-
GceIngress, CockroachDbCluster,
|
|
24
|
+
GceIngress, CockroachDbCluster, CockroachDbRegionStack,
|
|
25
25
|
RayCluster, RayJob, RayService,
|
|
26
26
|
AgicIngress, AzureDiskStorageClass, AzureFileStorageClass, AzureMonitorCollector,
|
|
27
27
|
AksWorkloadIdentityServiceAccount,
|
|
@@ -45,6 +45,7 @@ export type {
|
|
|
45
45
|
GkeGatewayProps, GkeGatewayResult,
|
|
46
46
|
ConfigConnectorContextProps, ConfigConnectorContextResult,
|
|
47
47
|
GceIngressProps, GceIngressResult, CockroachDbClusterProps, CockroachDbClusterResult,
|
|
48
|
+
CockroachDbRegionStackConfig, CockroachDbRegionCockroachConfig, CockroachDbRegionTlsConfig,
|
|
48
49
|
RayClusterProps, RayClusterResult, RayClusterSpec, ResourceSpec, HeadGroupSpec, WorkerGroupSpec,
|
|
49
50
|
RayJobProps, RayJobResult,
|
|
50
51
|
RayServiceProps, RayServiceResult,
|
package/src/plugin.test.ts
CHANGED
|
@@ -177,13 +177,13 @@ describe("k8sPlugin", () => {
|
|
|
177
177
|
test("mcpTools() returns diff tool", () => {
|
|
178
178
|
const tools = k8sPlugin.mcpTools!();
|
|
179
179
|
expect(Array.isArray(tools)).toBe(true);
|
|
180
|
-
expect(tools.some((t) => t.name === "diff")).toBe(true);
|
|
180
|
+
expect(tools.some((t) => t.name === "k8s:diff")).toBe(true);
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
test("mcpResources() returns resource-catalog and examples", () => {
|
|
184
184
|
const resources = k8sPlugin.mcpResources!();
|
|
185
185
|
expect(Array.isArray(resources)).toBe(true);
|
|
186
|
-
expect(resources.some((r) => r.uri === "resource-catalog")).toBe(true);
|
|
186
|
+
expect(resources.some((r) => r.uri === "k8s:resource-catalog")).toBe(true);
|
|
187
187
|
expect(resources.some((r) => r.uri === "examples/basic-deployment")).toBe(true);
|
|
188
188
|
});
|
|
189
189
|
|
package/src/plugin.ts
CHANGED
|
@@ -364,12 +364,12 @@ export const service = new Service({
|
|
|
364
364
|
},
|
|
365
365
|
|
|
366
366
|
mcpTools() {
|
|
367
|
-
return [createDiffTool(k8sSerializer, "Compare current build output against previous output for Kubernetes manifests")];
|
|
367
|
+
return [createDiffTool(k8sSerializer, "Compare current build output against previous output for Kubernetes manifests", "k8s")];
|
|
368
368
|
},
|
|
369
369
|
|
|
370
370
|
mcpResources() {
|
|
371
371
|
return [
|
|
372
|
-
createCatalogResource(import.meta.url, "Kubernetes Resource Catalog", "JSON list of all supported Kubernetes resource types", "lexicon-k8s.json"),
|
|
372
|
+
createCatalogResource(import.meta.url, "Kubernetes Resource Catalog", "JSON list of all supported Kubernetes resource types", "lexicon-k8s.json", "k8s"),
|
|
373
373
|
{
|
|
374
374
|
uri: "examples/basic-deployment",
|
|
375
375
|
name: "Basic Deployment Example",
|
|
@@ -657,4 +657,9 @@ const { deployment, service, serviceMonitor, prometheusRule } = MonitoredService
|
|
|
657
657
|
],
|
|
658
658
|
},
|
|
659
659
|
]),
|
|
660
|
+
|
|
661
|
+
async describeResources(options) {
|
|
662
|
+
const { describeResources } = await import("./describe-resources");
|
|
663
|
+
return describeResources(options);
|
|
664
|
+
},
|
|
660
665
|
};
|