@intentius/behold 0.7.0 → 0.9.0
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/AGENTS.md +94 -2
- package/README.md +133 -2
- package/demos.json +25 -1
- package/dist/cli.js +3176 -330
- package/example-argo-estate/README.md +71 -0
- package/example-argo-estate/app-a/chant.config.ts +18 -0
- package/example-argo-estate/app-a/manifests/app.yaml +54 -0
- package/example-argo-estate/app-a/package.json +13 -0
- package/example-argo-estate/app-a/src/app.ts +35 -0
- package/example-argo-estate/app-a/tsconfig.json +1 -0
- package/example-argo-estate/app-b/chant.config.ts +18 -0
- package/example-argo-estate/app-b/manifests/app.yaml +54 -0
- package/example-argo-estate/app-b/package.json +13 -0
- package/example-argo-estate/app-b/src/app.ts +34 -0
- package/example-argo-estate/app-b/tsconfig.json +1 -0
- package/example-argo-estate/control-plane/chant.config.ts +29 -0
- package/example-argo-estate/control-plane/package.json +13 -0
- package/example-argo-estate/control-plane/src/argo.ts +69 -0
- package/example-argo-estate/control-plane/tsconfig.json +1 -0
- package/example-argo-estate/package-lock.json +1949 -0
- package/example-argo-estate/package.json +10 -0
- package/example-carve/README.md +194 -0
- package/example-carve/app/chant.config.ts +6 -0
- package/example-carve/app/package-lock.json +1075 -0
- package/example-carve/app/package.json +13 -0
- package/example-carve/app/src/carved.ts +30 -0
- package/example-carve/app/tsconfig.json +1 -0
- package/example-carve/carve-report.json +872 -0
- package/example-carve/legacy-tf/cdn.tf +23 -0
- package/example-carve/legacy-tf/compute.tf +63 -0
- package/example-carve/legacy-tf/floci-override.tf.disabled +61 -0
- package/example-carve/legacy-tf/modules/cdn/main.tf +72 -0
- package/example-carve/legacy-tf/naming.tf +10 -0
- package/example-carve/legacy-tf/network.tf +119 -0
- package/example-carve/legacy-tf/observability.tf +18 -0
- package/example-carve/legacy-tf/outputs.tf +16 -0
- package/example-carve/legacy-tf/storage.tf +33 -0
- package/example-carve/legacy-tf/terraform.tfstate +602 -0
- package/example-carve/legacy-tf/versions.tf +40 -0
- package/example-flux-estate/README.md +22 -12
- package/example-flux-estate/app-a/package.json +2 -2
- package/example-flux-estate/app-a/src/app.ts +2 -1
- package/example-flux-estate/app-b/chant.config.ts +4 -3
- package/example-flux-estate/app-b/package.json +2 -2
- package/example-flux-estate/app-b/src/app.ts +5 -3
- package/example-flux-estate/control-plane/package.json +2 -2
- package/example-flux-estate/control-plane/src/flux.ts +10 -2
- package/example-flux-estate/package-lock.json +17 -17
- package/example-k8s/README.md +16 -2
- package/example-k8s/base/deployment.yaml +21 -0
- package/example-k8s/base/kustomization.yaml +3 -0
- package/example-k8s/base/service.yaml +12 -0
- package/example-k8s/chant.config.ts +10 -0
- package/example-k8s/overlays/dev/kustomization.yaml +6 -0
- package/example-k8s/package-lock.json +18 -18
- package/example-k8s/package.json +3 -3
- package/example-writes/package-lock.json +14 -14
- package/example-writes/package.json +3 -3
- package/package.json +9 -6
- package/web/app.js +1027 -42
- package/web/carve-steps.js +610 -0
- package/web/carve-steps.test.js +233 -0
- package/web/demos.js +71 -0
- package/web/demos.test.js +83 -0
- package/web/icons/cncf/argo.svg +1 -0
- package/web/icons/cncf/flux.svg +1 -0
- package/web/icons/cncf/helm.svg +1 -0
- package/web/icons/k8s/c-role.svg +1 -0
- package/web/icons/k8s/cm.svg +1 -0
- package/web/icons/k8s/crb.svg +1 -0
- package/web/icons/k8s/crd.svg +1 -0
- package/web/icons/k8s/cronjob.svg +1 -0
- package/web/icons/k8s/deploy.svg +1 -0
- package/web/icons/k8s/ds.svg +1 -0
- package/web/icons/k8s/ep.svg +1 -0
- package/web/icons/k8s/group.svg +1 -0
- package/web/icons/k8s/hpa.svg +1 -0
- package/web/icons/k8s/ing.svg +1 -0
- package/web/icons/k8s/job.svg +1 -0
- package/web/icons/k8s/limits.svg +1 -0
- package/web/icons/k8s/netpol.svg +1 -0
- package/web/icons/k8s/ns.svg +1 -0
- package/web/icons/k8s/pod.svg +1 -0
- package/web/icons/k8s/psp.svg +1 -0
- package/web/icons/k8s/pv.svg +1 -0
- package/web/icons/k8s/pvc.svg +1 -0
- package/web/icons/k8s/quota.svg +1 -0
- package/web/icons/k8s/rb.svg +1 -0
- package/web/icons/k8s/role.svg +1 -0
- package/web/icons/k8s/rs.svg +1 -0
- package/web/icons/k8s/sa.svg +1 -0
- package/web/icons/k8s/sc.svg +1 -0
- package/web/icons/k8s/secret.svg +1 -0
- package/web/icons/k8s/sts.svg +1 -0
- package/web/icons/k8s/svc.svg +1 -0
- package/web/icons/k8s/user.svg +1 -0
- package/web/icons/k8s/vol.svg +1 -0
- package/web/index.html +285 -85
- package/web/json-view.js +334 -0
- package/web/json-view.test.js +218 -0
- package/web/layout-store.js +278 -0
- package/web/layout-store.test.js +388 -0
- package/web/panel.js +28 -0
- package/web/theme.js +114 -6
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# argo-estate — behold's Argo CD estate demo (#235)
|
|
2
|
+
|
|
3
|
+
Three chant projects served as one composed estate, the Argo mirror of
|
|
4
|
+
[example-flux-estate](../example-flux-estate):
|
|
5
|
+
|
|
6
|
+
- **control-plane/** — the Argo machinery: an `AppProject` fencing which repo
|
|
7
|
+
and which namespaces the estate may deploy, and one `Application` per app
|
|
8
|
+
syncing that app's committed manifests from behold's own public repo. Argo's
|
|
9
|
+
controller does the applying; this project never applies a workload itself.
|
|
10
|
+
- **app-a/**, **app-b/** — small web workloads in namespaces `app-a` and
|
|
11
|
+
`app-b`, the namespaces the Applications name as their sync destinations.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx @intentius/behold demo argo-estate
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The demo path is **declared only** — no cluster, no Argo install, no Docker. It
|
|
18
|
+
copies out, `npm install`s, and serves. (There is a live lane, but it is an
|
|
19
|
+
acceptance run rather than part of the demo: see "Live" below.) What to look
|
|
20
|
+
at:
|
|
21
|
+
|
|
22
|
+
- The **estate**: per-project boundary boxes, and the `project` edges joining
|
|
23
|
+
both Applications to the AppProject they name (behold#222) — the join chant
|
|
24
|
+
lints as ARGO002, so an estate whose lint passes cannot be drawn wrong.
|
|
25
|
+
The entity graph carries them at the attributes tier (`?detail=3`).
|
|
26
|
+
- The **logical lens** (zoom: logical): cluster ⊃ `namespace app-a` and
|
|
27
|
+
`namespace app-b`, each holding that app's Deployment + Service, plus
|
|
28
|
+
`namespace argocd` holding both Applications and the AppProject — drawn
|
|
29
|
+
even though the control plane declares no `Namespace` object anywhere. The
|
|
30
|
+
app namespaces come from each Application's `spec.destination.namespace`; a
|
|
31
|
+
namespace Argo will own objects in is a namespace the estate is committed
|
|
32
|
+
to, and behold#224 taught the composed estate to draw that projection.
|
|
33
|
+
Serve `control-plane` alone (`behold serve control-plane`, zoom: logical)
|
|
34
|
+
and the same boxes appear empty — the destination harvest still names them,
|
|
35
|
+
but there is nothing from app-a/app-b in the graph to fill them with.
|
|
36
|
+
|
|
37
|
+
That alone-vs-composed split is the Argo/Flux difference worth noticing:
|
|
38
|
+
flux-estate's control plane must declare the app namespaces itself (a
|
|
39
|
+
Kustomization's `targetNamespace` must already exist), while here
|
|
40
|
+
`CreateNamespace=true` means Argo makes them — so the box exists on the
|
|
41
|
+
strength of the destination alone.
|
|
42
|
+
|
|
43
|
+
Argo's ordering is the `argocd.argoproj.io/sync-wave` annotation, so unlike
|
|
44
|
+
Flux's `dependsOn` (behold#223) there is no edge to draw for it: app-a is wave
|
|
45
|
+
0, app-b wave 1, and the picture says nothing about it.
|
|
46
|
+
|
|
47
|
+
The app `manifests/` are committed `chant build` output — the path each
|
|
48
|
+
Application syncs. Changing an app's source means a rebuild (`npm run build` in
|
|
49
|
+
the app), and the rebuilt manifests have to be on `main` before Argo can sync
|
|
50
|
+
them. `chant build` in the control plane warns ARGO005 on both Applications:
|
|
51
|
+
`source.path` is repo-relative (that's what Argo resolves it against), not
|
|
52
|
+
relative to the build root, so it doesn't resolve locally. Fine here, and the
|
|
53
|
+
check's own message says so.
|
|
54
|
+
|
|
55
|
+
## Live
|
|
56
|
+
|
|
57
|
+
Nothing above needs a cluster, but the estate as committed does reconcile
|
|
58
|
+
against a real one — the Applications point at this repo on `main` and the app
|
|
59
|
+
`manifests/` are already there, so an Argo CD install has everything it needs.
|
|
60
|
+
`just e2e-argo-estate` (behold#269) is that run: a scratch k3d cluster, Argo
|
|
61
|
+
from the pinned `core-install` manifest, the control plane applied once, and
|
|
62
|
+
then the composed estate served against the result — the destination-namespace
|
|
63
|
+
boxes filled with live cards, #238's health verdicts read off each
|
|
64
|
+
Application's health/sync pair, and the unhappy arm (an Application pointed at
|
|
65
|
+
a path that does not exist, then restored). It creates and deletes its own
|
|
66
|
+
cluster and touches no other context.
|
|
67
|
+
|
|
68
|
+
Each member's `chant.config.ts` therefore binds a `local` profile to the
|
|
69
|
+
context `k3d-behold-argo-demo`, which is what that run aliases its scratch
|
|
70
|
+
cluster to. Serving without `--env` — what `behold demo argo-estate` does —
|
|
71
|
+
reads none of it.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChantConfig } from "@intentius/chant";
|
|
2
|
+
import type { K8sChantConfig } from "@intentius/chant-lexicon-k8s";
|
|
3
|
+
|
|
4
|
+
// App project A. Argo syncs its committed manifests/ (chant build output) from
|
|
5
|
+
// the repo; this project applies nothing itself. behold serves it as part of
|
|
6
|
+
// the estate, and the live overlay reads the cluster through the same binding
|
|
7
|
+
// as every other member — see ../control-plane/chant.config.ts.
|
|
8
|
+
export default {
|
|
9
|
+
lexicons: ["k8s"],
|
|
10
|
+
sourceDir: "src",
|
|
11
|
+
environments: ["local"],
|
|
12
|
+
ownership: { stack: "argo-app-a" },
|
|
13
|
+
k8s: {
|
|
14
|
+
profiles: {
|
|
15
|
+
local: { context: "k3d-behold-argo-demo" },
|
|
16
|
+
},
|
|
17
|
+
} satisfies K8sChantConfig,
|
|
18
|
+
} satisfies ChantConfig;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: app-a
|
|
5
|
+
namespace: app-a
|
|
6
|
+
labels:
|
|
7
|
+
app.kubernetes.io/managed-by: chant
|
|
8
|
+
chant.intentius.io/stack: argo-app-a
|
|
9
|
+
app: app-a
|
|
10
|
+
spec:
|
|
11
|
+
replicas: 1
|
|
12
|
+
selector:
|
|
13
|
+
matchLabels:
|
|
14
|
+
app: app-a
|
|
15
|
+
template:
|
|
16
|
+
metadata:
|
|
17
|
+
labels:
|
|
18
|
+
app: app-a
|
|
19
|
+
spec:
|
|
20
|
+
containers:
|
|
21
|
+
- name: web
|
|
22
|
+
image: nginxinc/nginx-unprivileged:1.27-alpine
|
|
23
|
+
ports:
|
|
24
|
+
- containerPort: 8080
|
|
25
|
+
securityContext:
|
|
26
|
+
runAsNonRoot: true
|
|
27
|
+
runAsUser: 101
|
|
28
|
+
allowPrivilegeEscalation: false
|
|
29
|
+
capabilities:
|
|
30
|
+
drop:
|
|
31
|
+
- ALL
|
|
32
|
+
resources:
|
|
33
|
+
requests:
|
|
34
|
+
cpu: '25m'
|
|
35
|
+
memory: '32Mi'
|
|
36
|
+
limits:
|
|
37
|
+
cpu: '100m'
|
|
38
|
+
memory: '64Mi'
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
apiVersion: v1
|
|
42
|
+
kind: Service
|
|
43
|
+
metadata:
|
|
44
|
+
name: app-a
|
|
45
|
+
namespace: app-a
|
|
46
|
+
labels:
|
|
47
|
+
app.kubernetes.io/managed-by: chant
|
|
48
|
+
chant.intentius.io/stack: argo-app-a
|
|
49
|
+
spec:
|
|
50
|
+
selector:
|
|
51
|
+
app: app-a
|
|
52
|
+
ports:
|
|
53
|
+
- port: 80
|
|
54
|
+
targetPort: 8080
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "behold-example-argo-app-a",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "App project A: a small web workload in namespace app-a, synced by the control plane's Argo Application.",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "chant build src -o manifests/app.yaml --format yaml"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@intentius/chant": "^0.44.6",
|
|
11
|
+
"@intentius/chant-lexicon-k8s": "^0.44.6"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// App A: one small web workload, namespace declared on every object — the same
|
|
2
|
+
// namespace the control plane's Application names as its sync destination.
|
|
3
|
+
// That agreement is the point: behold draws the namespace box from the
|
|
4
|
+
// Application's destination and fills it with these cards.
|
|
5
|
+
import { Deployment, Service } from "@intentius/chant-lexicon-k8s";
|
|
6
|
+
|
|
7
|
+
const labels = { app: "app-a" };
|
|
8
|
+
const image = "nginxinc/nginx-unprivileged:1.27-alpine";
|
|
9
|
+
|
|
10
|
+
export const deployment = new Deployment({
|
|
11
|
+
metadata: { name: "app-a", namespace: "app-a", labels },
|
|
12
|
+
spec: {
|
|
13
|
+
replicas: 1,
|
|
14
|
+
selector: { matchLabels: labels },
|
|
15
|
+
template: {
|
|
16
|
+
metadata: { labels },
|
|
17
|
+
spec: {
|
|
18
|
+
containers: [
|
|
19
|
+
{
|
|
20
|
+
name: "web",
|
|
21
|
+
image,
|
|
22
|
+
ports: [{ containerPort: 8080 }],
|
|
23
|
+
securityContext: { runAsNonRoot: true, runAsUser: 101, allowPrivilegeEscalation: false, capabilities: { drop: ["ALL"] } },
|
|
24
|
+
resources: { requests: { cpu: "25m", memory: "32Mi" }, limits: { cpu: "100m", memory: "64Mi" } },
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const service = new Service({
|
|
33
|
+
metadata: { name: "app-a", namespace: "app-a" },
|
|
34
|
+
spec: { selector: labels, ports: [{ port: 80, targetPort: 8080 }] },
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "noEmit": true }, "include": ["src/**/*", "ops/**/*", "chant.config.ts"] }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChantConfig } from "@intentius/chant";
|
|
2
|
+
import type { K8sChantConfig } from "@intentius/chant-lexicon-k8s";
|
|
3
|
+
|
|
4
|
+
// App project B — same shape as app-a, second sync wave. Unlike flux-estate's
|
|
5
|
+
// app-b this one declares its own namespace on every object (Argo has no
|
|
6
|
+
// targetNamespace to stamp one at apply time), so a live read resolves it
|
|
7
|
+
// straight away. See ../control-plane/chant.config.ts for the binding.
|
|
8
|
+
export default {
|
|
9
|
+
lexicons: ["k8s"],
|
|
10
|
+
sourceDir: "src",
|
|
11
|
+
environments: ["local"],
|
|
12
|
+
ownership: { stack: "argo-app-b" },
|
|
13
|
+
k8s: {
|
|
14
|
+
profiles: {
|
|
15
|
+
local: { context: "k3d-behold-argo-demo" },
|
|
16
|
+
},
|
|
17
|
+
} satisfies K8sChantConfig,
|
|
18
|
+
} satisfies ChantConfig;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: app-b
|
|
5
|
+
namespace: app-b
|
|
6
|
+
labels:
|
|
7
|
+
app.kubernetes.io/managed-by: chant
|
|
8
|
+
chant.intentius.io/stack: argo-app-b
|
|
9
|
+
app: app-b
|
|
10
|
+
spec:
|
|
11
|
+
replicas: 2
|
|
12
|
+
selector:
|
|
13
|
+
matchLabels:
|
|
14
|
+
app: app-b
|
|
15
|
+
template:
|
|
16
|
+
metadata:
|
|
17
|
+
labels:
|
|
18
|
+
app: app-b
|
|
19
|
+
spec:
|
|
20
|
+
containers:
|
|
21
|
+
- name: web
|
|
22
|
+
image: nginxinc/nginx-unprivileged:1.27-alpine
|
|
23
|
+
ports:
|
|
24
|
+
- containerPort: 8080
|
|
25
|
+
securityContext:
|
|
26
|
+
runAsNonRoot: true
|
|
27
|
+
runAsUser: 101
|
|
28
|
+
allowPrivilegeEscalation: false
|
|
29
|
+
capabilities:
|
|
30
|
+
drop:
|
|
31
|
+
- ALL
|
|
32
|
+
resources:
|
|
33
|
+
requests:
|
|
34
|
+
cpu: '25m'
|
|
35
|
+
memory: '32Mi'
|
|
36
|
+
limits:
|
|
37
|
+
cpu: '100m'
|
|
38
|
+
memory: '64Mi'
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
apiVersion: v1
|
|
42
|
+
kind: Service
|
|
43
|
+
metadata:
|
|
44
|
+
name: app-b
|
|
45
|
+
namespace: app-b
|
|
46
|
+
labels:
|
|
47
|
+
app.kubernetes.io/managed-by: chant
|
|
48
|
+
chant.intentius.io/stack: argo-app-b
|
|
49
|
+
spec:
|
|
50
|
+
selector:
|
|
51
|
+
app: app-b
|
|
52
|
+
ports:
|
|
53
|
+
- port: 80
|
|
54
|
+
targetPort: 8080
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "behold-example-argo-app-b",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "App project B: the same workload in namespace app-b, synced one wave later.",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "chant build src -o manifests/app.yaml --format yaml"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@intentius/chant": "^0.44.6",
|
|
11
|
+
"@intentius/chant-lexicon-k8s": "^0.44.6"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// App B: the same workload in namespace app-b, the estate's second sync wave.
|
|
2
|
+
// Two apps rather than one so the AppProject has more than a single Application
|
|
3
|
+
// hanging off it — the fan-out is what an app-of-apps picture is for.
|
|
4
|
+
import { Deployment, Service } from "@intentius/chant-lexicon-k8s";
|
|
5
|
+
|
|
6
|
+
const labels = { app: "app-b" };
|
|
7
|
+
const image = "nginxinc/nginx-unprivileged:1.27-alpine";
|
|
8
|
+
|
|
9
|
+
export const deployment = new Deployment({
|
|
10
|
+
metadata: { name: "app-b", namespace: "app-b", labels },
|
|
11
|
+
spec: {
|
|
12
|
+
replicas: 2,
|
|
13
|
+
selector: { matchLabels: labels },
|
|
14
|
+
template: {
|
|
15
|
+
metadata: { labels },
|
|
16
|
+
spec: {
|
|
17
|
+
containers: [
|
|
18
|
+
{
|
|
19
|
+
name: "web",
|
|
20
|
+
image,
|
|
21
|
+
ports: [{ containerPort: 8080 }],
|
|
22
|
+
securityContext: { runAsNonRoot: true, runAsUser: 101, allowPrivilegeEscalation: false, capabilities: { drop: ["ALL"] } },
|
|
23
|
+
resources: { requests: { cpu: "25m", memory: "32Mi" }, limits: { cpu: "100m", memory: "64Mi" } },
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const service = new Service({
|
|
32
|
+
metadata: { name: "app-b", namespace: "app-b" },
|
|
33
|
+
spec: { selector: labels, ports: [{ port: 80, targetPort: 8080 }] },
|
|
34
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "noEmit": true }, "include": ["src/**/*", "ops/**/*", "chant.config.ts"] }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChantConfig } from "@intentius/chant";
|
|
2
|
+
import type { K8sChantConfig } from "@intentius/chant-lexicon-k8s";
|
|
3
|
+
|
|
4
|
+
// The estate's control plane: it declares the Argo machinery (an AppProject +
|
|
5
|
+
// one Application per app), and Argo's application controller deploys the app
|
|
6
|
+
// projects — this project never applies a workload itself.
|
|
7
|
+
//
|
|
8
|
+
// `npx @intentius/behold demo argo-estate` still needs no cluster: it serves
|
|
9
|
+
// the source graph with no `--env`, and nothing below is read on that path.
|
|
10
|
+
// The `local` profile exists for the live lane (behold#269,
|
|
11
|
+
// `just e2e-argo-estate`), which stands up a scratch k3d cluster and aliases
|
|
12
|
+
// its context to the name pinned here — so a live read resolves the DECLARED
|
|
13
|
+
// binding and can never observe whatever kubectl context happens to be
|
|
14
|
+
// ambient (chant#1100).
|
|
15
|
+
//
|
|
16
|
+
// Deliberately no `ownership.env`: the app members' committed manifests/ are
|
|
17
|
+
// what Argo syncs from `main`, so a label that changes their build output
|
|
18
|
+
// would have to be merged before any live run could agree with the cluster.
|
|
19
|
+
export default {
|
|
20
|
+
lexicons: ["k8s"],
|
|
21
|
+
sourceDir: "src",
|
|
22
|
+
environments: ["local"],
|
|
23
|
+
ownership: { stack: "argo-control-plane" },
|
|
24
|
+
k8s: {
|
|
25
|
+
profiles: {
|
|
26
|
+
local: { context: "k3d-behold-argo-demo" },
|
|
27
|
+
},
|
|
28
|
+
} satisfies K8sChantConfig,
|
|
29
|
+
} satisfies ChantConfig;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "behold-example-argo-control-plane",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The estate's control plane: the Argo CD CRs (an AppProject + one Application per app project).",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "chant build src -o dist/control-plane.yaml --format yaml"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@intentius/chant": "^0.44.6",
|
|
11
|
+
"@intentius/chant-lexicon-k8s": "^0.44.6"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// The control plane, Argo-style: one AppProject that fences what the estate is
|
|
2
|
+
// allowed to deploy, and one Application per app project, each syncing that
|
|
3
|
+
// app's committed manifests from this repo. Argo's application controller does
|
|
4
|
+
// the applying — this project never applies a workload itself.
|
|
5
|
+
//
|
|
6
|
+
// Written with the raw declarables rather than the ArgoAppFor composite on
|
|
7
|
+
// purpose: the two fields the whole demo is about, `spec.project` and
|
|
8
|
+
// `spec.destination.namespace`, are the ones a stranger reading Argo YAML
|
|
9
|
+
// already knows by sight, and here they sit in the source verbatim. (For real
|
|
10
|
+
// estates the composite is fewer lines: ArgoAppFor("app-a", { repo, path,
|
|
11
|
+
// project, destination }) renders this same Application.)
|
|
12
|
+
import { AppProject, Application } from "@intentius/chant-lexicon-k8s";
|
|
13
|
+
|
|
14
|
+
const REPO = "https://github.com/INTENTIUS/behold";
|
|
15
|
+
// The in-cluster target every Argo install has. Naming it (rather than a
|
|
16
|
+
// registered cluster Secret) is what keeps this estate a one-cluster story.
|
|
17
|
+
const IN_CLUSTER = "https://kubernetes.default.svc";
|
|
18
|
+
|
|
19
|
+
// The fence: which repo the apps may sync from, and which namespaces Argo may
|
|
20
|
+
// own objects in. Both Applications name it in spec.project — the join behold
|
|
21
|
+
// draws (behold#222) and chant lints as ARGO002.
|
|
22
|
+
export const project = new AppProject({
|
|
23
|
+
metadata: { name: "estate", namespace: "argocd" },
|
|
24
|
+
spec: {
|
|
25
|
+
description: "behold's demo estate: two app projects, one cluster.",
|
|
26
|
+
sourceRepos: [REPO],
|
|
27
|
+
destinations: [
|
|
28
|
+
{ server: IN_CLUSTER, namespace: "app-a" },
|
|
29
|
+
{ server: IN_CLUSTER, namespace: "app-b" },
|
|
30
|
+
],
|
|
31
|
+
// CreateNamespace=true below means Argo creates the destination namespaces,
|
|
32
|
+
// so the project has to permit that one cluster-scoped kind.
|
|
33
|
+
clusterResourceWhitelist: [{ group: "", kind: "Namespace" }],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const appA = new Application({
|
|
38
|
+
metadata: {
|
|
39
|
+
name: "app-a",
|
|
40
|
+
namespace: "argocd",
|
|
41
|
+
// Argo's ordering idiom is a wave annotation, not a reference — so unlike
|
|
42
|
+
// Flux's dependsOn (behold#223) there is no edge to draw for it. app-a
|
|
43
|
+
// syncs first; app-b follows.
|
|
44
|
+
annotations: { "argocd.argoproj.io/sync-wave": "0" },
|
|
45
|
+
},
|
|
46
|
+
spec: {
|
|
47
|
+
project: "estate",
|
|
48
|
+
source: { repoURL: REPO, targetRevision: "main", path: "example-argo-estate/app-a/manifests" },
|
|
49
|
+
// The namespace Argo will own objects in. app-a's own objects declare it
|
|
50
|
+
// too, so the box behold draws holds the app's cards; declaring it here is
|
|
51
|
+
// what makes the box a fact about the CONTROL PLANE's graph as well.
|
|
52
|
+
destination: { server: IN_CLUSTER, namespace: "app-a" },
|
|
53
|
+
syncPolicy: { automated: { prune: true, selfHeal: true }, syncOptions: ["CreateNamespace=true"] },
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const appB = new Application({
|
|
58
|
+
metadata: {
|
|
59
|
+
name: "app-b",
|
|
60
|
+
namespace: "argocd",
|
|
61
|
+
annotations: { "argocd.argoproj.io/sync-wave": "1" },
|
|
62
|
+
},
|
|
63
|
+
spec: {
|
|
64
|
+
project: "estate",
|
|
65
|
+
source: { repoURL: REPO, targetRevision: "main", path: "example-argo-estate/app-b/manifests" },
|
|
66
|
+
destination: { server: IN_CLUSTER, namespace: "app-b" },
|
|
67
|
+
syncPolicy: { automated: { prune: true, selfHeal: true }, syncOptions: ["CreateNamespace=true"] },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "noEmit": true }, "include": ["src/**/*", "ops/**/*", "chant.config.ts"] }
|