From ba11630d5658c0912e4a63f178b7ae6759809d0d Mon Sep 17 00:00:00 2001 From: ION606 Date: Wed, 27 Aug 2025 19:32:09 -0400 Subject: [PATCH] added initial istio BLAHAJ code --- .gitignore | 2 + apps/blahaj-common/app.mjs | 31 ++++++++++ apps/blahaj-common/bun.Dockerfile | 6 ++ apps/bubbles/deployment.yaml | 30 ++++++++++ apps/bubbles/kustomization.yaml | 3 + apps/coral/deployment.yaml | 30 ++++++++++ apps/coral/kustomization.yaml | 3 + apps/kelp/deployment.yaml | 30 ++++++++++ apps/kelp/kustomization.yaml | 5 ++ apps/reef/Dockerfile | 6 ++ apps/reef/deployment.yaml | 26 +++++++++ apps/reef/kustomization.yaml | 3 + apps/reef/public/app.js | 44 +++++++++++++++ apps/reef/public/index.html | 23 ++++++++ apps/reef/public/style.css | 94 +++++++++++++++++++++++++++++++ apps/reef/server.mjs | 16 ++++++ environments/dev/app-kelp.yaml | 17 ++++++ environments/dev/app-root.yaml | 17 ++++++ environments/dev/gateway.yaml | 11 ++++ environments/dev/httproute.yaml | 18 ++++++ environments/dev/namespace.yaml | 6 ++ run.sh | 17 ++++++ 22 files changed, 438 insertions(+) create mode 100644 .gitignore create mode 100644 apps/blahaj-common/app.mjs create mode 100644 apps/blahaj-common/bun.Dockerfile create mode 100644 apps/bubbles/deployment.yaml create mode 100644 apps/bubbles/kustomization.yaml create mode 100644 apps/coral/deployment.yaml create mode 100644 apps/coral/kustomization.yaml create mode 100644 apps/kelp/deployment.yaml create mode 100644 apps/kelp/kustomization.yaml create mode 100644 apps/reef/Dockerfile create mode 100644 apps/reef/deployment.yaml create mode 100644 apps/reef/kustomization.yaml create mode 100644 apps/reef/public/app.js create mode 100644 apps/reef/public/index.html create mode 100644 apps/reef/public/style.css create mode 100644 apps/reef/server.mjs create mode 100644 environments/dev/app-kelp.yaml create mode 100644 environments/dev/app-root.yaml create mode 100644 environments/dev/gateway.yaml create mode 100644 environments/dev/httproute.yaml create mode 100644 environments/dev/namespace.yaml create mode 100644 run.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43cf4ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.txt +*.xml diff --git a/apps/blahaj-common/app.mjs b/apps/blahaj-common/app.mjs new file mode 100644 index 0000000..038d99b --- /dev/null +++ b/apps/blahaj-common/app.mjs @@ -0,0 +1,31 @@ +// bun, es modules, minimal deps; one file serves all three services +import http from 'http'; + +const serviceName = process.env.SERVICE_NAME || 'blahaj'; +const emoji = process.env.SERVICE_EMOJI || '🦈'; +const friend = process.env.FRIEND_NAME || 'sea-friend'; +const port = Number(process.env.PORT || 8080); + +const ascii = ` + __ + _.-' \\ + _.-' _.-'\\) ${emoji} // ascii blahaj says hi! + (____.-' ~ waves ~ +`; + +const server = http.createServer((req, res) => { + const path = req.url || '/'; + const message = { + service: serviceName, + says: `hello, ${friend}! i am ${serviceName} ✨`, + path, + mascot: 'BLÅHAJ', + fun: 'soft, cuddly shark energy', + }; + res.setHeader('content-type', 'application/json; charset=utf-8'); + res.end(JSON.stringify({ message, ascii })); +}); + +server.listen(port, () => { + console.log(`${serviceName} listening on ${port};`); +}); diff --git a/apps/blahaj-common/bun.Dockerfile b/apps/blahaj-common/bun.Dockerfile new file mode 100644 index 0000000..8df934d --- /dev/null +++ b/apps/blahaj-common/bun.Dockerfile @@ -0,0 +1,6 @@ +FROM oven/bun:1.1.31-alpine AS base +WORKDIR /app +COPY app.mjs ./ +EXPOSE 8080 +ENV NODE_ENV=production +CMD ["bun", "run", "app.mjs"] diff --git a/apps/bubbles/deployment.yaml b/apps/bubbles/deployment.yaml new file mode 100644 index 0000000..8a54965 --- /dev/null +++ b/apps/bubbles/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bubbles + namespace: demo +spec: + replicas: 2 + selector: { matchLabels: { app: bubbles } } + template: + metadata: + labels: { app: bubbles } + spec: + containers: + - name: app + image: blahaj-bun:dev + imagePullPolicy: IfNotPresent + ports: [{ containerPort: 8080, name: http }] + env: + - { name: SERVICE_NAME, value: "bubbles" } + - { name: SERVICE_EMOJI, value: "🫧" } + - { name: FRIEND_NAME, value: "coral" } +--- +apiVersion: v1 +kind: Service +metadata: + name: bubbles + namespace: demo +spec: + selector: { app: bubbles } + ports: [{ name: http, port: 80, targetPort: 8080 }] diff --git a/apps/bubbles/kustomization.yaml b/apps/bubbles/kustomization.yaml new file mode 100644 index 0000000..492f0af --- /dev/null +++ b/apps/bubbles/kustomization.yaml @@ -0,0 +1,3 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: [deployment.yaml, service.yaml] diff --git a/apps/coral/deployment.yaml b/apps/coral/deployment.yaml new file mode 100644 index 0000000..8b0d5b6 --- /dev/null +++ b/apps/coral/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coral + namespace: demo +spec: + replicas: 2 + selector: { matchLabels: { app: coral } } + template: + metadata: + labels: { app: coral } + spec: + containers: + - name: app + image: blahaj-bun:dev + imagePullPolicy: IfNotPresent + ports: [{ containerPort: 8080, name: http }] + env: + - { name: SERVICE_NAME, value: "coral" } + - { name: SERVICE_EMOJI, value: "🪸" } + - { name: FRIEND_NAME, value: "kelp" } +--- +apiVersion: v1 +kind: Service +metadata: + name: coral + namespace: demo +spec: + selector: { app: coral } + ports: [{ name: http, port: 80, targetPort: 8080 }] diff --git a/apps/coral/kustomization.yaml b/apps/coral/kustomization.yaml new file mode 100644 index 0000000..492f0af --- /dev/null +++ b/apps/coral/kustomization.yaml @@ -0,0 +1,3 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: [deployment.yaml, service.yaml] diff --git a/apps/kelp/deployment.yaml b/apps/kelp/deployment.yaml new file mode 100644 index 0000000..03a0d07 --- /dev/null +++ b/apps/kelp/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kelp + namespace: demo +spec: + replicas: 2 + selector: { matchLabels: { app: kelp } } + template: + metadata: + labels: { app: kelp } + spec: + containers: + - name: app + image: blahaj-bun:dev + imagePullPolicy: IfNotPresent + ports: [{ containerPort: 8080, name: http }] + env: + - { name: SERVICE_NAME, value: "kelp" } + - { name: SERVICE_EMOJI, value: "🌿" } + - { name: FRIEND_NAME, value: "blahaj" } +--- +apiVersion: v1 +kind: Service +metadata: + name: kelp + namespace: demo +spec: + selector: { app: kelp } + ports: [{ name: http, port: 80, targetPort: 8080 }] diff --git a/apps/kelp/kustomization.yaml b/apps/kelp/kustomization.yaml new file mode 100644 index 0000000..ab948eb --- /dev/null +++ b/apps/kelp/kustomization.yaml @@ -0,0 +1,5 @@ +# eh I wanted to try it + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: [deployment.yaml, service.yaml] diff --git a/apps/reef/Dockerfile b/apps/reef/Dockerfile new file mode 100644 index 0000000..7a042fd --- /dev/null +++ b/apps/reef/Dockerfile @@ -0,0 +1,6 @@ +FROM oven/bun:1.1.31-alpine +WORKDIR /app +COPY server.mjs ./server.mjs +COPY public ./public +EXPOSE 8080 +CMD ["bun", "run", "server.mjs"] diff --git a/apps/reef/deployment.yaml b/apps/reef/deployment.yaml new file mode 100644 index 0000000..2db7ee0 --- /dev/null +++ b/apps/reef/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: reef + namespace: demo +spec: + replicas: 1 + selector: { matchLabels: { app: reef } } + template: + metadata: + labels: { app: reef } + spec: + containers: + - name: app + image: blahaj-bun:dev + imagePullPolicy: IfNotPresent + ports: [{ name: http, containerPort: 8080 }] +--- +apiVersion: v1 +kind: Service +metadata: + name: reef + namespace: demo +spec: + selector: { app: reef } + ports: [{ name: http, port: 80, targetPort: 8080 }] diff --git a/apps/reef/kustomization.yaml b/apps/reef/kustomization.yaml new file mode 100644 index 0000000..6cb0a5d --- /dev/null +++ b/apps/reef/kustomization.yaml @@ -0,0 +1,3 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: [deployment.yaml] diff --git a/apps/reef/public/app.js b/apps/reef/public/app.js new file mode 100644 index 0000000..cb2d54b --- /dev/null +++ b/apps/reef/public/app.js @@ -0,0 +1,44 @@ +const endpoints = [ + { path: '/blahaj', title: 'bubbles greeter (blahaj)' }, + { path: '/kelp', title: 'kelp' }, + { path: '/coral', title: 'coral' }, + { path: '/bubbles', title: 'bubbles' }, +]; + +const cards = document.querySelector('#cards'); + +function cardDOM({ title, ok, data, error }) { + const el = document.createElement('article'); + el.className = `card ${ok ? 'ok' : 'err'}`; + + const h2 = document.createElement('h2'); + h2.textContent = title; + el.appendChild(h2); + + const pre = document.createElement('pre'); + pre.textContent = ok ? JSON.stringify(data, null, 2) : String(error || 'error'); + el.appendChild(pre); + + return el; +} + +async function fetchOne(path) { + const r = await fetch(path, { headers: { 'accept': 'application/json' } }); + if (!r.ok) throw new Error(`${path} -> ${r.status}`); + return r.json(); +} + +async function refresh() { + cards.textContent = ''; // clear + for (const ep of endpoints) { + try { + const data = await fetchOne(ep.path); + cards.appendChild(cardDOM({ title: ep.title, ok: true, data })); + } catch (err) { + cards.appendChild(cardDOM({ title: ep.title, ok: false, error: err.message })); + } + } +} + +document.querySelector('#refresh').addEventListener('click', () => refresh()); +refresh(); diff --git a/apps/reef/public/index.html b/apps/reef/public/index.html new file mode 100644 index 0000000..c8b7ee8 --- /dev/null +++ b/apps/reef/public/index.html @@ -0,0 +1,23 @@ + + + + + blåhaj reef + + + + +
+

🦈 blåhaj’s reef

+

cute sea-friends saying hi (live from your mesh)

+
+ +
+ + + + + + diff --git a/apps/reef/public/style.css b/apps/reef/public/style.css new file mode 100644 index 0000000..e699321 --- /dev/null +++ b/apps/reef/public/style.css @@ -0,0 +1,94 @@ +/* soft, cute card ui */ +/* created by ChatGPT because god help me I can't UI */ +:root { + --bg: #0b132b; + --panel: #1c2541; + --ink: #e0e6ff; + --muted: #9fb3ff; + --ok: #3ddc97; + --err: #ff6b6b; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Inter, sans-serif; + background: var(--bg); + color: var(--ink); +} + +header { + padding: 24px; + text-align: center; +} + +header h1 { + margin: 0 0 8px 0; + font-size: 28px; +} + +header p { + margin: 0; + color: var(--muted); +} + +#cards { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + padding: 16px; + max-width: 1200px; + margin: 0 auto; +} + +.card { + background: var(--panel); + border-radius: 16px; + padding: 16px; + box-shadow: 0 10px 24px rgba(0, 0, 0, .25); + border: 1px solid rgba(255, 255, 255, .06); +} + +.card.ok { + outline: 2px solid var(--ok); +} + +.card.err { + outline: 2px solid var(--err); +} + +.card h2 { + margin: 0 0 8px 0; + font-size: 18px; +} + +.card pre { + margin: 0; + white-space: pre-wrap; + word-break: break-word; + font-size: 13px; + color: #e9f2ff; +} + +footer { + display: flex; + justify-content: center; + padding: 16px 0 32px; +} + +button { + background: var(--ok); + color: #002b36; + border: 0; + padding: 10px 16px; + border-radius: 12px; + font-weight: 600; + cursor: pointer; +} + +button:hover { + filter: brightness(.95); +} \ No newline at end of file diff --git a/apps/reef/server.mjs b/apps/reef/server.mjs new file mode 100644 index 0000000..7702270 --- /dev/null +++ b/apps/reef/server.mjs @@ -0,0 +1,16 @@ +const serveFile = (path) => new Response(Bun.file(path)); + +const server = Bun.serve({ + port: Number(process.env.PORT || 8080), + async fetch(req) { + const url = new URL(req.url); + if (url.pathname === '/') return serveFile('./public/index.html'); + if (url.pathname.startsWith('/static/')) { + return serveFile(`.${url.pathname}`); + } + + return serveFile('./public/index.html'); + }, +}); + +console.log(`reef listening on ${server.port};`); diff --git a/environments/dev/app-kelp.yaml b/environments/dev/app-kelp.yaml new file mode 100644 index 0000000..cbeeb46 --- /dev/null +++ b/environments/dev/app-kelp.yaml @@ -0,0 +1,17 @@ +# THE C H I L D +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: app-kelp + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/ION606/argo-temp.git + targetRevision: blahaj-app + path: apps/kelp + destination: + server: https://kubernetes.default.svc + namespace: demo + syncPolicy: + automated: { prune: true, selfHeal: true } diff --git a/environments/dev/app-root.yaml b/environments/dev/app-root.yaml new file mode 100644 index 0000000..2799f9e --- /dev/null +++ b/environments/dev/app-root.yaml @@ -0,0 +1,17 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: blahaj-root + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/ION606/argo-temp.git + targetRevision: blahaj-app + path: environments/dev + directory: { recurse: true } + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: { prune: true, selfHeal: true } diff --git a/environments/dev/gateway.yaml b/environments/dev/gateway.yaml new file mode 100644 index 0000000..bb306f0 --- /dev/null +++ b/environments/dev/gateway.yaml @@ -0,0 +1,11 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: blahaj-gw + namespace: demo +spec: + gatewayClassName: istio + listeners: + - name: http + port: 80 + protocol: HTTP diff --git a/environments/dev/httproute.yaml b/environments/dev/httproute.yaml new file mode 100644 index 0000000..23f33b2 --- /dev/null +++ b/environments/dev/httproute.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: blahaj-routes + namespace: demo +spec: + parentRefs: [{ name: blahaj-gw }] + rules: + - matches: [{ path: { type: PathPrefix, value: "/" } }] + backendRefs: [{ name: reef, port: 80 }] + - matches: [{ path: { type: PathPrefix, value: "/kelp" } }] + backendRefs: [{ name: kelp, port: 80 }] + - matches: [{ path: { type: PathPrefix, value: "/coral" } }] + backendRefs: [{ name: coral, port: 80 }] + - matches: [{ path: { type: PathPrefix, value: "/bubbles" } }] + backendRefs: [{ name: bubbles, port: 80 }] + - matches: [{ path: { type: PathPrefix, value: "/blahaj" } }] + backendRefs: [{ name: bubbles, port: 80 }] diff --git a/environments/dev/namespace.yaml b/environments/dev/namespace.yaml new file mode 100644 index 0000000..56e8fa8 --- /dev/null +++ b/environments/dev/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: demo + labels: + istio.io/dataplane-mode: ambient diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..ecee300 --- /dev/null +++ b/run.sh @@ -0,0 +1,17 @@ +# build locally and and load into minikube +docker build -t blahaj-bun:dev -f apps/blahaj-common/bun.Dockerfile apps/blahaj-common; +docker build -t blahaj-bun:dev -f apps/reef/Dockerfile apps/reef; +minikube image load blahaj-bun:dev; + +# apply k8s (plus existing kelp/coral/bubbles apps) +kubectl apply -f apps/reef/kustomization.yaml -n demo --server-side --force-conflicts || true; +kubectl -n demo apply -f apps/reef/deployment.yaml; +kubectl -n demo apply -f apps/reef/kustomization.yaml; + +# ensure gateway + route exist (from env files) +kubectl -n demo apply -f environments/dev/gateway.yaml; +kubectl -n demo apply -f environments/dev/httproute.yaml; + +# open the gateway service +minikube service -n istio-gateway -l gateway.istio.io/managed=yes --url +# isit http://:80/ (cards!)