diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 904259d..d4df1c8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,22 +20,8 @@ jobs:
with:
go-version-file: go.mod
- - name: Setup Helm
- uses: azure/setup-helm@v4
-
- name: Build
run: go build ./...
- name: Test
run: go test ./...
-
- - name: Helm lint
- run: helm lint chart/agentikube/
-
- - name: Helm template
- run: |
- helm template agentikube chart/agentikube/ \
- --namespace sandboxes \
- --set storage.filesystemId=fs-test \
- --set sandbox.image=test:latest \
- --set compute.clusterName=test-cluster
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b754e7b..bbf975f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,6 @@ on:
permissions:
contents: write
- packages: write
concurrency:
group: release-${{ github.ref }}
@@ -30,9 +29,6 @@ jobs:
with:
go-version-file: go.mod
- - name: Setup Helm
- uses: azure/setup-helm@v4
-
- name: Compute next version
id: version
shell: bash
@@ -97,17 +93,3 @@ jobs:
name: ${{ steps.version.outputs.next_tag }}
generate_release_notes: true
files: dist/*
-
- - name: Set chart version
- run: |
- sed -i "s/^version:.*/version: ${{ steps.version.outputs.version }}/" chart/agentikube/Chart.yaml
- sed -i "s/^appVersion:.*/appVersion: \"${{ steps.version.outputs.version }}\"/" chart/agentikube/Chart.yaml
-
- - name: Package Helm chart
- run: helm package chart/agentikube/ --destination .helm-pkg
-
- - name: Log in to GHCR
- run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
-
- - name: Push Helm chart to GHCR
- run: helm push .helm-pkg/agentikube-${{ steps.version.outputs.version }}.tgz oci://ghcr.io/${{ github.repository_owner }}
diff --git a/.gitignore b/.gitignore
index 16d3c4d..6198882 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
.cache
+agentikube
diff --git a/Makefile b/Makefile
index f68ed93..160de4a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: build install clean fmt vet lint crds helm-lint helm-template
+.PHONY: build install clean fmt vet lint
build:
go build -o agentikube ./cmd/agentikube
@@ -16,16 +16,3 @@ vet:
go vet ./...
lint: fmt vet
-
-crds:
- ./scripts/download-crds.sh
-
-helm-lint:
- helm lint chart/agentikube/
-
-helm-template:
- helm template agentikube chart/agentikube/ \
- --namespace sandboxes \
- --set storage.filesystemId=fs-test \
- --set sandbox.image=test:latest \
- --set compute.clusterName=test-cluster
diff --git a/README.md b/README.md
index d566e76..296157a 100644
--- a/README.md
+++ b/README.md
@@ -1,85 +1,80 @@
# agentikube
[](https://github.com/harivansh-afk/agentikube/blob/main/go.mod)
-[](https://github.com/harivansh-afk/agentikube/tree/main/chart/agentikube)
[](https://github.com/harivansh-afk/agentikube/releases/latest)
-Isolated stateful agent sandboxes on Kubernetes
+A small Go CLI that spins up isolated agent sandboxes on Kubernetes. Built for AWS setups (EFS + optional Karpenter).
-
+
-## Install
+## What it does
+
+- **`init`** - Installs CRDs, checks prerequisites, ensures your namespace exists
+- **`up`** - Renders and applies Kubernetes manifests from templates (`--dry-run` to preview)
+- **`create `** - Spins up a sandbox for a user with provider credentials
+- **`list`** - Shows all sandboxes with status, age, and pod name
+- **`status`** - Warm pool numbers, sandbox count, Karpenter node count
+- **`ssh `** - Drops you into a sandbox pod shell
+- **`destroy `** - Tears down a single sandbox
+- **`down`** - Removes shared infra but keeps existing user sandboxes
+
+## Quick start
```bash
-helm install agentikube oci://ghcr.io/harivansh-afk/agentikube \
- -n sandboxes --create-namespace \
- -f my-values.yaml
-```
+# 1. Copy and fill in your config
+cp agentikube.example.yaml agentikube.yaml
+# Edit: namespace, EFS filesystem ID, sandbox image, compute settings
-Create a `my-values.yaml` with your cluster details:
+# 2. Set things up
+agentikube init
+agentikube up
-```yaml
-compute:
- clusterName: my-eks-cluster
-storage:
- filesystemId: fs-0123456789abcdef0
-sandbox:
- image: my-registry/sandbox:latest
-```
-
-See [`values.yaml`](chart/agentikube/values.yaml) for all options.
-
-## CLI
-
-The Go CLI handles runtime operations that are inherently imperative:
-
-```bash
+# 3. Create a sandbox and jump in
agentikube create demo --provider openai --api-key
agentikube list
agentikube ssh demo
-agentikube status
-agentikube destroy demo
```
-Build it with `go build ./cmd/agentikube` or `make build`.
-
## What gets created
-The Helm chart installs:
+Running `up` applies these to your cluster:
-- StorageClass (`efs-sandbox`) backed by your EFS filesystem
-- SandboxTemplate defining the pod spec
-- NetworkPolicy for ingress/egress rules
-- SandboxWarmPool (optional, enabled by default)
-- Karpenter NodePool + EC2NodeClass (optional, when `compute.type: karpenter`)
+- Namespace, StorageClass (`efs-sandbox`), SandboxTemplate
+- Optionally: SandboxWarmPool, NodePool + EC2NodeClass (Karpenter)
-Each `agentikube create ` then adds a Secret, SandboxClaim, and workspace PVC for that user.
+Running `create ` adds:
+
+- A Secret and SandboxClaim per user
+- A workspace PVC backed by EFS
## Project layout
```
-cmd/agentikube/ CLI entrypoint
-internal/ config, manifest rendering, kube helpers
-chart/agentikube/ Helm chart
-scripts/ CRD download helper
+cmd/agentikube/main.go # entrypoint
+internal/config/ # config structs + validation
+internal/manifest/ # template rendering
+internal/manifest/templates/ # k8s YAML templates
+internal/kube/ # kube client helpers
+internal/commands/ # command implementations
+agentikube.example.yaml # example config
+Makefile # build/install/fmt/vet
```
-## Development
+## Build and test locally
```bash
-make build # compile CLI
-make helm-lint # lint the chart
-make helm-template # dry-run render
-go test ./... # run tests
+go build ./...
+go test ./...
+go run ./cmd/agentikube --help
+
+# Smoke test manifest generation
+./agentikube up --dry-run --config agentikube.example.yaml
```
## Good to know
-- Storage is EFS-only for now
-- `kubectl` must be installed (used by `init` and `ssh`)
-- Fargate is validated in config but templates only cover Karpenter so far
-- [k9s](https://k9scli.io/) is great for browsing sandbox resources
-
-## Context
-
-(https://harivan.sh/thoughts/isolated-long-running-agents-with-kubernetes)
+- `storage.type` is `efs` only for now
+- `kubectl` needs to be installed (used by `init` and `ssh`)
+- Fargate is validated in config but templates only cover the Karpenter path so far
+- No Go tests written yet - `go test` passes but reports no test files
+- [k9s](https://k9scli.io/) is great for browsing sandbox resources (`brew install derailed/k9s/k9s`)
diff --git a/agentikube b/agentikube
deleted file mode 100755
index 83f4562..0000000
Binary files a/agentikube and /dev/null differ
diff --git a/chart/agentikube/Chart.yaml b/chart/agentikube/Chart.yaml
deleted file mode 100644
index 293a85d..0000000
--- a/chart/agentikube/Chart.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: v2
-name: agentikube
-description: Isolated agent sandboxes on Kubernetes
-type: application
-version: 0.1.0
-appVersion: "0.1.0"
-keywords:
- - sandbox
- - agents
- - kubernetes
- - karpenter
- - efs
diff --git a/chart/agentikube/crds/.gitkeep b/chart/agentikube/crds/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/chart/agentikube/templates/NOTES.txt b/chart/agentikube/templates/NOTES.txt
deleted file mode 100644
index 1fcd08a..0000000
--- a/chart/agentikube/templates/NOTES.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-agentikube has been installed in namespace {{ .Release.Namespace }}.
-
-Resources created:
- - StorageClass: efs-sandbox (EFS filesystem: {{ .Values.storage.filesystemId }})
- - SandboxTemplate: sandbox-template
-{{- if .Values.sandbox.warmPool.enabled }}
- - SandboxWarmPool: sandbox-warm-pool ({{ .Values.sandbox.warmPool.size }} replicas)
-{{- end }}
-{{- if eq .Values.compute.type "karpenter" }}
- - NodePool: sandbox-pool
- - EC2NodeClass: sandbox-nodes
-{{- end }}
- - NetworkPolicy: sandbox-network-policy
-
-To create a sandbox:
- agentikube create --provider --api-key
-
-To list sandboxes:
- agentikube list
-
-To SSH into a sandbox:
- agentikube ssh
-
-To destroy a sandbox:
- agentikube destroy
diff --git a/chart/agentikube/templates/_helpers.tpl b/chart/agentikube/templates/_helpers.tpl
deleted file mode 100644
index c5210b9..0000000
--- a/chart/agentikube/templates/_helpers.tpl
+++ /dev/null
@@ -1,42 +0,0 @@
-{{/*
-Expand the name of the chart.
-*/}}
-{{- define "agentikube.name" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
-{{- end }}
-
-{{/*
-Create a default fully qualified app name.
-*/}}
-{{- define "agentikube.fullname" -}}
-{{- if .Values.fullnameOverride }}
-{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- $name := default .Chart.Name .Values.nameOverride }}
-{{- if contains $name .Release.Name }}
-{{- .Release.Name | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
-{{- end }}
-{{- end }}
-{{- end }}
-
-{{/*
-Common labels
-*/}}
-{{- define "agentikube.labels" -}}
-helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
-{{ include "agentikube.selectorLabels" . }}
-{{- if .Chart.AppVersion }}
-app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
-{{- end }}
-app.kubernetes.io/managed-by: {{ .Release.Service }}
-{{- end }}
-
-{{/*
-Selector labels
-*/}}
-{{- define "agentikube.selectorLabels" -}}
-app.kubernetes.io/name: {{ include "agentikube.name" . }}
-app.kubernetes.io/instance: {{ .Release.Name }}
-{{- end }}
diff --git a/chart/agentikube/templates/karpenter-ec2nodeclass.yaml b/chart/agentikube/templates/karpenter-ec2nodeclass.yaml
deleted file mode 100644
index b2f4959..0000000
--- a/chart/agentikube/templates/karpenter-ec2nodeclass.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-{{- if eq .Values.compute.type "karpenter" }}
-apiVersion: karpenter.k8s.aws/v1
-kind: EC2NodeClass
-metadata:
- name: sandbox-nodes
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-spec:
- amiSelectorTerms:
- - alias: "al2023@latest"
- subnetSelectorTerms:
- - tags:
- karpenter.sh/discovery: {{ required "compute.clusterName is required for Karpenter" .Values.compute.clusterName | quote }}
- securityGroupSelectorTerms:
- - tags:
- karpenter.sh/discovery: {{ .Values.compute.clusterName | quote }}
- role: {{ printf "KarpenterNodeRole-%s" .Values.compute.clusterName | quote }}
-{{- end }}
diff --git a/chart/agentikube/templates/karpenter-nodepool.yaml b/chart/agentikube/templates/karpenter-nodepool.yaml
deleted file mode 100644
index 4d55cb4..0000000
--- a/chart/agentikube/templates/karpenter-nodepool.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-{{- if eq .Values.compute.type "karpenter" }}
-apiVersion: karpenter.sh/v1
-kind: NodePool
-metadata:
- name: sandbox-pool
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-spec:
- template:
- spec:
- requirements:
- - key: node.kubernetes.io/instance-type
- operator: In
- values:
- {{- range .Values.compute.instanceTypes }}
- - {{ . }}
- {{- end }}
- - key: karpenter.sh/capacity-type
- operator: In
- values:
- {{- range .Values.compute.capacityTypes }}
- - {{ . }}
- {{- end }}
- - key: kubernetes.io/arch
- operator: In
- values:
- - amd64
- nodeClassRef:
- name: sandbox-nodes
- group: karpenter.k8s.aws
- kind: EC2NodeClass
- limits:
- cpu: {{ .Values.compute.maxCpu }}
- memory: {{ .Values.compute.maxMemory }}
- disruption:
- consolidationPolicy: {{ if .Values.compute.consolidation }}WhenEmptyOrUnderutilized{{ else }}WhenEmpty{{ end }}
-{{- end }}
diff --git a/chart/agentikube/templates/networkpolicy.yaml b/chart/agentikube/templates/networkpolicy.yaml
deleted file mode 100644
index fbad38e..0000000
--- a/chart/agentikube/templates/networkpolicy.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: sandbox-network-policy
- namespace: {{ .Release.Namespace }}
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-spec:
- podSelector:
- matchLabels:
- app.kubernetes.io/name: sandbox
- policyTypes:
- - Ingress
- {{- if .Values.sandbox.networkPolicy.egressAllowAll }}
- - Egress
- {{- end }}
- {{- if .Values.sandbox.networkPolicy.egressAllowAll }}
- egress:
- - to:
- - ipBlock:
- cidr: 0.0.0.0/0
- {{- end }}
- ingress:
- {{- range .Values.sandbox.networkPolicy.ingressPorts }}
- - ports:
- - port: {{ . }}
- protocol: TCP
- {{- end }}
diff --git a/chart/agentikube/templates/sandbox-template.yaml b/chart/agentikube/templates/sandbox-template.yaml
deleted file mode 100644
index 2c61361..0000000
--- a/chart/agentikube/templates/sandbox-template.yaml
+++ /dev/null
@@ -1,57 +0,0 @@
-apiVersion: extensions.agents.x-k8s.io/v1alpha1
-kind: SandboxTemplate
-metadata:
- name: sandbox-template
- namespace: {{ .Release.Namespace }}
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-spec:
- template:
- spec:
- containers:
- - name: sandbox
- image: {{ required "sandbox.image is required" .Values.sandbox.image }}
- ports:
- {{- range .Values.sandbox.ports }}
- - containerPort: {{ . }}
- {{- end }}
- resources:
- requests:
- cpu: {{ .Values.sandbox.resources.requests.cpu }}
- memory: {{ .Values.sandbox.resources.requests.memory }}
- limits:
- cpu: {{ .Values.sandbox.resources.limits.cpu | quote }}
- memory: {{ .Values.sandbox.resources.limits.memory }}
- securityContext:
- runAsUser: {{ .Values.sandbox.securityContext.runAsUser }}
- runAsGroup: {{ .Values.sandbox.securityContext.runAsGroup }}
- runAsNonRoot: {{ .Values.sandbox.securityContext.runAsNonRoot }}
- {{- if .Values.sandbox.env }}
- env:
- {{- range $key, $value := .Values.sandbox.env }}
- - name: {{ $key }}
- value: {{ $value | quote }}
- {{- end }}
- {{- end }}
- startupProbe:
- tcpSocket:
- port: {{ .Values.sandbox.probes.port }}
- failureThreshold: {{ .Values.sandbox.probes.startupFailureThreshold }}
- periodSeconds: 10
- readinessProbe:
- tcpSocket:
- port: {{ .Values.sandbox.probes.port }}
- periodSeconds: 10
- volumeMounts:
- - name: workspace
- mountPath: {{ .Values.sandbox.mountPath }}
- volumeClaimTemplates:
- - metadata:
- name: workspace
- spec:
- accessModes:
- - ReadWriteMany
- storageClassName: efs-sandbox
- resources:
- requests:
- storage: "10Gi"
diff --git a/chart/agentikube/templates/storageclass-efs.yaml b/chart/agentikube/templates/storageclass-efs.yaml
deleted file mode 100644
index 8a9c2ff..0000000
--- a/chart/agentikube/templates/storageclass-efs.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: storage.k8s.io/v1
-kind: StorageClass
-metadata:
- name: efs-sandbox
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-provisioner: efs.csi.aws.com
-parameters:
- provisioningMode: efs-ap
- fileSystemId: {{ required "storage.filesystemId is required" .Values.storage.filesystemId }}
- directoryPerms: "755"
- uid: {{ .Values.storage.uid | quote }}
- gid: {{ .Values.storage.gid | quote }}
- basePath: {{ .Values.storage.basePath }}
-reclaimPolicy: {{ .Values.storage.reclaimPolicy }}
-volumeBindingMode: Immediate
diff --git a/chart/agentikube/templates/warm-pool.yaml b/chart/agentikube/templates/warm-pool.yaml
deleted file mode 100644
index 52f726f..0000000
--- a/chart/agentikube/templates/warm-pool.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-{{- if .Values.sandbox.warmPool.enabled }}
-apiVersion: extensions.agents.x-k8s.io/v1alpha1
-kind: SandboxWarmPool
-metadata:
- name: sandbox-warm-pool
- namespace: {{ .Release.Namespace }}
- labels:
- {{- include "agentikube.labels" . | nindent 4 }}
-spec:
- templateRef:
- name: sandbox-template
- replicas: {{ .Values.sandbox.warmPool.size }}
- ttlMinutes: {{ .Values.sandbox.warmPool.ttlMinutes }}
-{{- end }}
diff --git a/chart/agentikube/values.yaml b/chart/agentikube/values.yaml
deleted file mode 100644
index f40ece8..0000000
--- a/chart/agentikube/values.yaml
+++ /dev/null
@@ -1,66 +0,0 @@
-# Compute configuration for sandbox nodes
-compute:
- # karpenter or fargate
- type: karpenter
- instanceTypes:
- - m6i.xlarge
- - m5.xlarge
- - r6i.xlarge
- capacityTypes:
- - spot
- - on-demand
- maxCpu: 2000
- maxMemory: 8000Gi
- consolidation: true
- # EKS cluster name - used for Karpenter subnet/SG/role discovery
- clusterName: ""
-
-# Persistent storage configuration
-storage:
- # efs is the only supported type
- type: efs
- # REQUIRED - your EFS filesystem ID
- filesystemId: ""
- basePath: /sandboxes
- uid: 1000
- gid: 1000
- reclaimPolicy: Retain
-
-# Sandbox pod configuration
-sandbox:
- # REQUIRED - container image for sandbox pods
- image: ""
- ports:
- - 18789
- - 2222
- - 3000
- - 5173
- - 8080
- mountPath: /home/node/.openclaw
- resources:
- requests:
- cpu: 50m
- memory: 512Mi
- limits:
- cpu: "2"
- memory: 4Gi
- env: {}
- securityContext:
- runAsUser: 1000
- runAsGroup: 1000
- runAsNonRoot: true
- probes:
- port: 18789
- startupFailureThreshold: 30
- warmPool:
- enabled: true
- size: 5
- ttlMinutes: 120
- networkPolicy:
- egressAllowAll: true
- ingressPorts:
- - 18789
- - 2222
- - 3000
- - 5173
- - 8080
diff --git a/chart/agentikube_test.go b/chart/agentikube_test.go
deleted file mode 100644
index 9854572..0000000
--- a/chart/agentikube_test.go
+++ /dev/null
@@ -1,223 +0,0 @@
-package chart_test
-
-import (
- "os"
- "os/exec"
- "strings"
- "testing"
-)
-
-// helmTemplate runs helm template with the given extra args and returns stdout.
-func helmTemplate(t *testing.T, extraArgs ...string) string {
- t.Helper()
- args := []string{
- "template", "agentikube", "chart/agentikube/",
- "--namespace", "sandboxes",
- "--set", "storage.filesystemId=fs-test",
- "--set", "sandbox.image=test:latest",
- "--set", "compute.clusterName=test-cluster",
- }
- args = append(args, extraArgs...)
- cmd := exec.Command("helm", args...)
- cmd.Dir = repoRoot(t)
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("helm template failed: %v\n%s", err, out)
- }
- return string(out)
-}
-
-func repoRoot(t *testing.T) string {
- t.Helper()
- dir, err := os.Getwd()
- if err != nil {
- t.Fatal(err)
- }
- // This test file lives at chart/agentikube_test.go, so repo root is ..
- return dir + "/.."
-}
-
-func TestHelmLint(t *testing.T) {
- cmd := exec.Command("helm", "lint", "chart/agentikube/")
- cmd.Dir = repoRoot(t)
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("helm lint failed: %v\n%s", err, out)
- }
- if !strings.Contains(string(out), "0 chart(s) failed") {
- t.Fatalf("helm lint reported failures:\n%s", out)
- }
-}
-
-func TestHelmTemplateDefaultValues(t *testing.T) {
- output := helmTemplate(t)
-
- expected := []string{
- "kind: StorageClass",
- "kind: SandboxTemplate",
- "kind: SandboxWarmPool",
- "kind: NodePool",
- "kind: EC2NodeClass",
- "kind: NetworkPolicy",
- }
- for _, want := range expected {
- if !strings.Contains(output, want) {
- t.Errorf("expected %q in rendered output", want)
- }
- }
-}
-
-func TestHelmTemplateLabels(t *testing.T) {
- output := helmTemplate(t)
-
- labels := []string{
- "helm.sh/chart: agentikube-0.1.0",
- "app.kubernetes.io/name: agentikube",
- "app.kubernetes.io/instance: agentikube",
- "app.kubernetes.io/managed-by: Helm",
- `app.kubernetes.io/version: "0.1.0"`,
- }
- for _, label := range labels {
- if !strings.Contains(output, label) {
- t.Errorf("expected label %q in rendered output", label)
- }
- }
-}
-
-func TestHelmTemplateKarpenterDisabled(t *testing.T) {
- output := helmTemplate(t, "--set", "compute.type=fargate")
-
- if strings.Contains(output, "kind: NodePool") {
- t.Error("NodePool should not be rendered when compute.type=fargate")
- }
- if strings.Contains(output, "kind: EC2NodeClass") {
- t.Error("EC2NodeClass should not be rendered when compute.type=fargate")
- }
- if !strings.Contains(output, "kind: StorageClass") {
- t.Error("StorageClass should always be rendered")
- }
- if !strings.Contains(output, "kind: SandboxTemplate") {
- t.Error("SandboxTemplate should always be rendered")
- }
-}
-
-func TestHelmTemplateWarmPoolDisabled(t *testing.T) {
- output := helmTemplate(t, "--set", "sandbox.warmPool.enabled=false")
-
- if strings.Contains(output, "kind: SandboxWarmPool") {
- t.Error("SandboxWarmPool should not be rendered when warmPool.enabled=false")
- }
- if !strings.Contains(output, "kind: SandboxTemplate") {
- t.Error("SandboxTemplate should always be rendered")
- }
-}
-
-func TestHelmTemplateEgressDisabled(t *testing.T) {
- output := helmTemplate(t,
- "--set", "sandbox.networkPolicy.egressAllowAll=false",
- "-s", "templates/networkpolicy.yaml",
- )
-
- if strings.Contains(output, "0.0.0.0/0") {
- t.Error("egress CIDR should not appear when egressAllowAll=false")
- }
- lines := strings.Split(output, "\n")
- for i, line := range lines {
- if strings.Contains(line, "policyTypes:") {
- block := strings.Join(lines[i:min(i+4, len(lines))], "\n")
- if strings.Contains(block, "Egress") {
- t.Error("Egress should not be in policyTypes when egressAllowAll=false")
- }
- }
- }
-}
-
-func TestHelmTemplateRequiredValues(t *testing.T) {
- tests := []struct {
- name string
- args []string
- wantErr string
- }{
- {
- name: "missing filesystemId",
- args: []string{"--set", "sandbox.image=test:latest", "--set", "compute.clusterName=test"},
- wantErr: "storage.filesystemId is required",
- },
- {
- name: "missing sandbox image",
- args: []string{"--set", "storage.filesystemId=fs-test", "--set", "compute.clusterName=test"},
- wantErr: "sandbox.image is required",
- },
- {
- name: "missing clusterName for karpenter",
- args: []string{"--set", "storage.filesystemId=fs-test", "--set", "sandbox.image=test:latest"},
- wantErr: "compute.clusterName is required for Karpenter",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- args := append([]string{
- "template", "agentikube", "chart/agentikube/",
- "--namespace", "sandboxes",
- }, tt.args...)
- cmd := exec.Command("helm", args...)
- cmd.Dir = repoRoot(t)
- out, err := cmd.CombinedOutput()
- if err == nil {
- t.Fatal("expected helm template to fail for missing required value")
- }
- if !strings.Contains(string(out), tt.wantErr) {
- t.Errorf("expected error containing %q, got:\n%s", tt.wantErr, out)
- }
- })
- }
-}
-
-func TestHelmTemplateEnvVars(t *testing.T) {
- output := helmTemplate(t,
- "--set", "sandbox.env.MY_VAR=my-value",
- "-s", "templates/sandbox-template.yaml",
- )
-
- if !strings.Contains(output, "MY_VAR") {
- t.Error("expected MY_VAR in rendered env")
- }
- if !strings.Contains(output, "my-value") {
- t.Error("expected my-value in rendered env")
- }
-}
-
-func TestHelmTemplateNoEnvWhenEmpty(t *testing.T) {
- output := helmTemplate(t, "-s", "templates/sandbox-template.yaml")
-
- lines := strings.Split(output, "\n")
- for _, line := range lines {
- trimmed := strings.TrimSpace(line)
- if trimmed == "env:" {
- t.Error("env: block should not appear when sandbox.env is empty")
- }
- }
-}
-
-func TestHelmTemplateNamespace(t *testing.T) {
- output := helmTemplate(t, "--namespace", "custom-ns")
-
- if !strings.Contains(output, "namespace: custom-ns") {
- t.Error("expected namespace: custom-ns in rendered output")
- }
-}
-
-func TestHelmTemplateConsolidationDisabled(t *testing.T) {
- output := helmTemplate(t,
- "--set", "compute.consolidation=false",
- "-s", "templates/karpenter-nodepool.yaml",
- )
-
- if !strings.Contains(output, "consolidationPolicy: WhenEmpty") {
- t.Error("expected consolidationPolicy: WhenEmpty when consolidation=false")
- }
- if strings.Contains(output, "WhenEmptyOrUnderutilized") {
- t.Error("should not have WhenEmptyOrUnderutilized when consolidation=false")
- }
-}
diff --git a/scripts/download-crds.sh b/scripts/download-crds.sh
deleted file mode 100755
index f4090be..0000000
--- a/scripts/download-crds.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-# Download agent-sandbox CRDs into chart/agentikube/crds/
-# Run this before packaging the chart: make crds
-
-REPO="kubernetes-sigs/agent-sandbox"
-BRANCH="main"
-BASE_URL="https://raw.githubusercontent.com/${REPO}/${BRANCH}/k8s/crds"
-DEST="$(cd "$(dirname "$0")/.." && pwd)/chart/agentikube/crds"
-
-CRDS=(
- sandboxtemplates.yaml
- sandboxclaims.yaml
- sandboxwarmpools.yaml
-)
-
-echo "Downloading CRDs from ${REPO}@${BRANCH} ..."
-mkdir -p "$DEST"
-
-for crd in "${CRDS[@]}"; do
- echo " ${crd}"
- curl -sSfL "${BASE_URL}/${crd}" -o "${DEST}/${crd}"
-done
-
-echo "CRDs written to ${DEST}"