Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions app_dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import (

ctrl "sigs.k8s.io/controller-runtime"

"github.com/coder/coder-k8s/internal/app/allapp"
"github.com/coder/coder-k8s/internal/app/apiserverapp"
"github.com/coder/coder-k8s/internal/app/controllerapp"
"github.com/coder/coder-k8s/internal/app/mcpapp"
)

const supportedAppModes = "controller, aggregated-apiserver, mcp-http"
const supportedAppModes = "all, controller, aggregated-apiserver, mcp-http"

var (
runControllerApp = controllerapp.Run
runAggregatedAPIServerApp = func(ctx context.Context, opts apiserverapp.Options) error {
runAllApp func(context.Context, time.Duration) error = allapp.Run
runControllerApp = controllerapp.Run
runAggregatedAPIServerApp = func(ctx context.Context, opts apiserverapp.Options) error {
return apiserverapp.RunWithOptions(ctx, opts)
}
runMCPHTTPApp = mcpapp.RunHTTP
Expand All @@ -35,7 +37,7 @@ func run(args []string) error {
coderNamespace string
coderRequestTimeout time.Duration
)
fs.StringVar(&appMode, "app", "", "Application mode (controller, aggregated-apiserver, mcp-http)")
fs.StringVar(&appMode, "app", "all", "Application mode (all, controller, aggregated-apiserver, mcp-http)")
fs.StringVar(
&coderSessionToken,
"coder-session-token",
Expand Down Expand Up @@ -82,6 +84,8 @@ func run(args []string) error {
}

switch appMode {
case "all":
return runAllApp(setupSignalHandler(), coderRequestTimeout)
case "controller":
return runControllerApp(setupSignalHandler())
case "aggregated-apiserver":
Expand All @@ -94,8 +98,6 @@ func run(args []string) error {
return runAggregatedAPIServerApp(setupSignalHandler(), opts)
case "mcp-http":
return runMCPHTTPApp(setupSignalHandler())
case "":
return fmt.Errorf("assertion failed: --app flag is required; must be one of: %s", supportedAppModes)
default:
return fmt.Errorf("assertion failed: unsupported --app value %q; must be one of: %s", appMode, supportedAppModes)
}
Expand Down
2 changes: 2 additions & 0 deletions config/e2e/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ spec:
containers:
- name: manager
image: ghcr.io/coder/coder-k8s:e2e
# E2E tests validate controller mode only; all mode requires
# additional infrastructure (APIService, TLS) not provisioned here.
args: ["--app=controller"]
imagePullPolicy: Never
ports:
Expand Down
23 changes: 0 additions & 23 deletions deploy/apiserver-deployment.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion deploy/apiserver-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metadata:
namespace: coder-system
spec:
selector:
app: coder-k8s-apiserver
app: coder-k8s
ports:
- name: https
port: 443
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
apiVersion: apps/v1
kind: Deployment
Comment thread
ThomasK33 marked this conversation as resolved.
metadata:
name: coder-k8s-controller
name: coder-k8s
namespace: coder-system
spec:
replicas: 1
selector:
matchLabels:
app: coder-k8s-controller
app: coder-k8s
template:
metadata:
labels:
app: coder-k8s-controller
app: coder-k8s
spec:
serviceAccountName: coder-k8s-controller
serviceAccountName: coder-k8s
containers:
- name: controller
- name: coder-k8s
image: ghcr.io/coder/coder-k8s:latest
args: ["--app=controller"]
ports:
- containerPort: 8081
name: health
- containerPort: 6443
name: https
- containerPort: 8090
name: mcp
livenessProbe:
httpGet:
path: /healthz
Expand Down
31 changes: 0 additions & 31 deletions deploy/mcp-deployment.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion deploy/mcp-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metadata:
namespace: coder-system
spec:
selector:
app: coder-k8s-mcp
app: coder-k8s
ports:
- name: mcp
port: 8090
Expand Down
112 changes: 57 additions & 55 deletions deploy/rbac.yaml
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: coder-k8s-controller
name: coder-k8s
namespace: coder-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: coder-k8s-apiserver
namespace: coder-system
---
# ClusterRole for the controller to manage CoderControlPlane resources.
# Permissions match the kubebuilder RBAC markers on CoderControlPlaneReconciler.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: coder-k8s-controller
name: coder-k8s
rules:
# Controller: manage CoderControlPlane resources
- apiGroups: ["coder.com"]
resources: ["codercontrolplanes"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
Expand All @@ -26,81 +19,90 @@ rules:
- apiGroups: ["coder.com"]
resources: ["codercontrolplanes/finalizers"]
verbs: ["update"]
# Controller: manage WorkspaceProxy resources
- apiGroups: ["coder.com"]
resources: ["workspaceproxies"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["coder.com"]
resources: ["workspaceproxies/status"]
verbs: ["get", "update", "patch"]
- apiGroups: ["coder.com"]
resources: ["workspaceproxies/finalizers"]
verbs: ["update"]
# Controller: manage CoderProvisioner resources
- apiGroups: ["coder.com"]
resources: ["coderprovisioners"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["coder.com"]
resources: ["coderprovisioners/status"]
verbs: ["get", "update", "patch"]
- apiGroups: ["coder.com"]
resources: ["coderprovisioners/finalizers"]
verbs: ["update"]
# Controller: leader election
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Controller + MCP: events
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch", "create", "patch"]
# Controller: manage deployments and services for control planes
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Dynamic provider: read operator token secrets
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
# MCP: read aggregated API resources
- apiGroups: ["aggregation.coder.com"]
resources: ["coderworkspaces", "codertemplates"]
verbs: ["get", "list", "watch", "update", "patch"]
# MCP: read pods, logs, namespaces
- apiGroups: [""]
resources: ["pods", "pods/log", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: coder-k8s-controller
name: coder-k8s
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: coder-k8s-controller
name: coder-k8s
subjects:
- kind: ServiceAccount
name: coder-k8s-controller
name: coder-k8s
namespace: coder-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: coder-k8s-apiserver-auth-delegator
name: coder-k8s-auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: coder-k8s-apiserver
name: coder-k8s
namespace: coder-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: coder-k8s-apiserver-authentication-reader
name: coder-k8s-authentication-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: coder-k8s-apiserver
namespace: coder-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: coder-k8s-mcp
namespace: coder-system
---
# ClusterRole for the MCP server to read operator resources.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: coder-k8s-mcp
rules:
- apiGroups: ["coder.com"]
resources: ["codercontrolplanes", "codercontrolplanes/status"]
verbs: ["get", "list", "watch"]
- apiGroups: ["aggregation.coder.com"]
resources: ["coderworkspaces", "codertemplates"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services", "pods", "pods/log", "events", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: coder-k8s-mcp
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: coder-k8s-mcp
subjects:
- kind: ServiceAccount
name: coder-k8s-mcp
name: coder-k8s
namespace: coder-system
10 changes: 7 additions & 3 deletions docs/how-to/deploy-aggregated-apiserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ kubectl create namespace coder-system

## 2. Apply RBAC

The RBAC manifest includes service accounts for both the controller and the aggregated API server.
The RBAC manifest creates the unified `coder-k8s` ServiceAccount used by all app modes.

```bash
kubectl apply -f deploy/rbac.yaml
Expand All @@ -26,9 +26,13 @@ kubectl apply -f deploy/rbac.yaml

```bash
kubectl apply -f deploy/apiserver-service.yaml
kubectl apply -f deploy/apiserver-deployment.yaml
kubectl apply -f deploy/deployment.yaml
```

`deploy/deployment.yaml` defaults to `--app=all`, which runs the controller, aggregated API server, and MCP server in a single pod.

For split deployments, you can still run individual components by setting `--app=controller`, `--app=aggregated-apiserver`, or `--app=mcp-http` in the Deployment args.

## 4. Register the APIService

```bash
Expand All @@ -40,7 +44,7 @@ kubectl apply -f deploy/apiserver-apiservice.yaml
Wait for the deployment:

```bash
kubectl rollout status deployment/coder-k8s-apiserver -n coder-system
kubectl rollout status deployment/coder-k8s -n coder-system
```

Check the APIService:
Expand Down
12 changes: 8 additions & 4 deletions docs/how-to/deploy-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@ kubectl apply -f config/crd/bases/
kubectl apply -f deploy/rbac.yaml
```

## 4. Deploy the controller
## 4. Deploy `coder-k8s`

```bash
kubectl apply -f deploy/controller-deployment.yaml
kubectl apply -f deploy/deployment.yaml
```

`deploy/deployment.yaml` defaults to `--app=all`, which runs the controller, aggregated API server, and MCP server in a single pod.

For split deployments, you can still run individual components by setting `--app=controller`, `--app=aggregated-apiserver`, or `--app=mcp-http` in the Deployment args.

## 5. Verify

```bash
kubectl rollout status deployment/coder-k8s-controller -n coder-system
kubectl rollout status deployment/coder-k8s -n coder-system
kubectl get pods -n coder-system
```

## Customizing the image

By default, `deploy/controller-deployment.yaml` uses `ghcr.io/coder/coder-k8s:latest`. For a different image tag, edit the deployment manifest before applying it.
By default, `deploy/deployment.yaml` uses `ghcr.io/coder/coder-k8s:latest`. For a different image tag, edit the deployment manifest before applying it.
Loading