# Kubernetes workspaces demo
We are going to "workspace-ify" our Kubernetes cluster by defining 3 different workspaces within our multi-tenant cluster:
* `ops-team`
* `finance-team`
* `backend-team`
## Setup
### KUBECONFIG contexts
For this demo, we have a number of contexts setup:
- `kind-kind` - the 'real' Kubernetes cluster
- `wildcard` - points to `/clusters/*`
- `finance` - points to `/clusters/finance-team`
- `backend` - points to `/clusters/backend-team`
### Install CustomResourceDefinitions:
```shell
$ kubectl create --context kind-kind \
-f ./config/crds/rbac.authorization.clusterscoped.kcp.io_workspacerolebindings.yaml \
-f ./config/crds/rbac.authorization.clusterscoped.kcp.io_workspaceroles.yaml \
-f ./config/crds/tenancy.kcp.io_workspaceconfigurations.yaml
```
### Namespaces
We have a standard 'kind' cluster:
```shell
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro workspaces-apiserver % kubectl get namespaces
NAME STATUS AGE
default Active 4d6h
kube-node-lease Active 4d6h
kube-public Active 4d6h
kube-system Active 4d6h
local-path-storage Active 4d6h
```
We create a few dummy namespaces for our teams:
```shell
$ echo \
"finance-team-operators \
finance-team-jobs \
backend-webservices \
backend-searchservice \
backend-staging \
backend-development" | xargs -n 1 kubectl create namespace
```
We create 'system' namespaces for the three workspaces:
```shell
$ echo \
"workspace-scoped-ops-team \
workspace-scoped-finance-team \
workspace-scoped-backend-team" | xargs -n 1 kubectl create namespace
```
### Demo workloads
Create some demo workloads so we can all see what is going on:
```shell
# Create dummy workloads for the backend team
$ kubectl create deployment --image nginx --namespace backend-webservices webserver --replicas=3
$ kubectl create deployment --image nginx --namespace backend-searchservice mycool-search-tool --replicas=3
$ kubectl create deployment --image nginx --namespace backend-staging searchtool-staging --replicas=1
$ kubectl create deployment --image nginx --namespace backend-development searchtool-dev-newfeature --replicas=1
$ kubectl create deployment --image nginx --namespace backend-development searchtool-dev-coolthing --replicas=1
# Create dummy workloads for the finance team
$ kubectl create deployment --image nginx --namespace finance-team-operators moneymaking-operator --replicas=1
$ kubectl create cronjob --image nginx --namespace finance-team-jobs makemoney-job
```
### WorkspaceConfiguration
This is created in the `kind-kind` cluster and defines the mapping from workspace to namespaces (and where workspace-scoped objects are stored):
```yaml=
apiVersion: tenancy.kcp.io/v1alpha1
kind: WorkspaceConfiguration
metadata:
name: workspace-apiserver-config
spec:
workspaces:
- name: ops-team
systemNamespace: workspace-scoped-ops-team
namespaces:
- kube-system
- kube-public
- kube-node-lease
- local-path-storage
- name: finance-team
systemNamespace: workspace-scoped-finance-team
namespaces:
- finance-team-operators
- finance-team-jobs
- name: backend-team
systemNamespace: workspace-scoped-backend-team
namespaces:
- backend-webservices
- backend-searchservice
- backend-staging
- backend-development
```
> NOTE: This object is 'low level' and in future would be auto-managed by a controller watching for `Workspace` objects or something similar (tbd)
### Creating 'workspace admins'
```yaml=
# Ops admins
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRole
metadata:
name: workspace-admin
namespace: workspace-scoped-ops-team
rules:
- apiGroups:
- "*"
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRoleBinding
metadata:
name: workspace-admin
namespace: workspace-scoped-ops-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole # todo: this should be WorkspaceRole in future
name: workspace-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: james@munnelly.eu
---
# Finance admins
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRole
metadata:
name: workspace-admin
namespace: workspace-scoped-finance-team
rules:
- apiGroups:
- "*"
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRoleBinding
metadata:
name: workspace-admin
namespace: workspace-scoped-finance-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole # todo: this should be WorkspaceRole in future
name: workspace-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: james@munnelly.eu
---
# Backend admins
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRole
metadata:
name: workspace-admin
namespace: workspace-scoped-backend-team
rules:
- apiGroups:
- "*"
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.clusterscoped.kcp.io/v1
kind: WorkspaceRoleBinding
metadata:
name: workspace-admin
namespace: workspace-scoped-backend-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole # todo: this should be WorkspaceRole in future
name: workspace-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: james@munnelly.eu
```
### Running workspaces-apiserver
Get the apiserver running - we configure the OIDC authenticator at the moment, however you would typically configure to delegate authN to the real cluster (via `TokenReview`)
```shell=
$ ./workspaces-apiserver start \
--oidc-client-id=<clientid-here> \
--oidc-issuer-url=https://accounts.google.com \
--oidc-username-claim=email \
--workspace-configuration-name=workspace-apiserver-config \
--workspace-shard-name mycompany:us-west-1 \
--workspace-cluster-kubeconfig-file $HOME/.kube/config \
--workspace-cluster-context-name kind-kind
```
## Usage
With all of this setup, we can see the 'real' view:
```shell=
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get namespaces
NAME STATUS AGE
backend-development Active 37m
backend-searchservice Active 37m
backend-staging Active 37m
backend-webservices Active 38m
default Active 48m
finance-team-jobs Active 36m
finance-team-operators Active 36m
kube-node-lease Active 48m
kube-public Active 48m
kube-system Active 48m
local-path-storage Active 48m
workspace-scoped-backend-team Active 71s
workspace-scoped-finance-team Active 71s
workspace-scoped-ops-team Active 71s
```
### Via the 'wildcard' context
```shell
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get namespaces --context wildcard
NAME STATUS AGE
backend-development Active 26m
backend-searchservice Active 26m
backend-staging Active 26m
backend-webservices Active 26m
finance-team-jobs Active 24m
finance-team-operators Active 24m
kube-node-lease Active 37m
kube-public Active 37m
kube-system Active 37m
local-path-storage Active 36m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get all --context wildcard --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE WORKSPACE
backend-development pod/searchtool-dev-coolthing-f4d4b8f87-42vc7 1/1 Running 0 13m backend-team
backend-development pod/searchtool-dev-newfeature-d94bbb8d-b6hn7 1/1 Running 0 14m backend-team
backend-searchservice pod/mycool-search-tool-ccc89d7d4-f9zdz 1/1 Running 0 14m backend-team
backend-searchservice pod/mycool-search-tool-ccc89d7d4-p7xl6 1/1 Running 0 14m backend-team
backend-searchservice pod/mycool-search-tool-ccc89d7d4-s9czj 1/1 Running 0 14m backend-team
backend-staging pod/searchtool-staging-5f8c55bb8c-5cbzf 1/1 Running 0 14m backend-team
backend-webservices pod/webserver-667ddc69b6-94bss 1/1 Running 0 15m backend-team
backend-webservices pod/webserver-667ddc69b6-bh4t5 1/1 Running 0 15m backend-team
backend-webservices pod/webserver-667ddc69b6-hmqtm 1/1 Running 0 15m backend-team
finance-team-jobs pod/makemoney-job-28615114-2p7dg 1/1 Running 0 10m finance-team
finance-team-jobs pod/makemoney-job-28615115-mb4hn 1/1 Running 0 9m39s finance-team
finance-team-jobs pod/makemoney-job-28615116-sk7d9 1/1 Running 0 8m39s finance-team
finance-team-jobs pod/makemoney-job-28615117-bl8hk 1/1 Running 0 7m39s finance-team
finance-team-jobs pod/makemoney-job-28615118-bnvlt 1/1 Running 0 6m39s finance-team
finance-team-jobs pod/makemoney-job-28615119-v8nt4 1/1 Running 0 5m39s finance-team
finance-team-jobs pod/makemoney-job-28615120-76vtk 1/1 Running 0 4m39s finance-team
finance-team-jobs pod/makemoney-job-28615121-g5nnc 1/1 Running 0 3m39s finance-team
finance-team-jobs pod/makemoney-job-28615122-xqvlr 1/1 Running 0 2m39s finance-team
finance-team-jobs pod/makemoney-job-28615123-7kb5b 1/1 Running 0 99s finance-team
finance-team-jobs pod/makemoney-job-28615124-9qpng 1/1 Running 0 39s finance-team
finance-team-operators pod/moneymaking-operator-67f995d6f7-52zf4 1/1 Running 0 12m finance-team
kube-system pod/coredns-76f75df574-2gnfj 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/coredns-76f75df574-8rq7f 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/etcd-kind-control-plane 1/1 Running 0 36m ops-team
kube-system pod/kindnet-59vvq 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/kindnet-zb5kv 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/kube-apiserver-kind-control-plane 1/1 Running 0 36m ops-team
kube-system pod/kube-controller-manager-kind-control-plane 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/kube-proxy-9q5ps 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/kube-proxy-ljthv 1/1 Running 1 (36m ago) 41m ops-team
kube-system pod/kube-scheduler-kind-control-plane 1/1 Running 1 (36m ago) 41m ops-team
local-path-storage pod/local-path-provisioner-7577fdbbfb-5jzd4 1/1 Running 2 (36m ago) 41m ops-team
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE WORKSPACE
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 41m ops-team
NAMESPACE NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE WORKSPACE
finance-team-jobs cronjob.batch/makemoney-job */1 * * * * False 11 39s 11m finance-team
NAMESPACE NAME COMPLETIONS DURATION AGE WORKSPACE
finance-team-jobs job.batch/makemoney-job-28615114 0/1 10m 10m finance-team
finance-team-jobs job.batch/makemoney-job-28615115 0/1 9m39s 9m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615116 0/1 8m39s 8m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615117 0/1 7m39s 7m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615118 0/1 6m39s 6m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615119 0/1 5m39s 5m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615120 0/1 4m39s 4m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615121 0/1 3m39s 3m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615122 0/1 2m39s 2m39s finance-team
finance-team-jobs job.batch/makemoney-job-28615123 0/1 99s 99s finance-team
finance-team-jobs job.batch/makemoney-job-28615124 0/1 39s 39s finance-team
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE WORKSPACE
kube-system daemonset.apps/kindnet 2 2 2 2 2 kubernetes.io/os=linux 41m ops-team
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 41m ops-team
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE WORKSPACE
backend-development deployment.apps/searchtool-dev-coolthing 1/1 1 1 13m backend-team
backend-development deployment.apps/searchtool-dev-newfeature 1/1 1 1 14m backend-team
backend-searchservice deployment.apps/mycool-search-tool 3/3 3 3 14m backend-team
backend-staging deployment.apps/searchtool-staging 1/1 1 1 14m backend-team
backend-webservices deployment.apps/webserver 3/3 3 3 15m backend-team
finance-team-operators deployment.apps/moneymaking-operator 1/1 1 1 12m finance-team
kube-system deployment.apps/coredns 2/2 2 2 41m ops-team
local-path-storage deployment.apps/local-path-provisioner 1/1 1 1 41m ops-team
NAMESPACE NAME DESIRED CURRENT READY AGE WORKSPACE
backend-development replicaset.apps/searchtool-dev-coolthing-f4d4b8f87 1 1 1 13m backend-team
backend-development replicaset.apps/searchtool-dev-newfeature-d94bbb8d 1 1 1 14m backend-team
backend-searchservice replicaset.apps/mycool-search-tool-ccc89d7d4 3 3 3 14m backend-team
backend-staging replicaset.apps/searchtool-staging-5f8c55bb8c 1 1 1 14m backend-team
backend-webservices replicaset.apps/webserver-667ddc69b6 3 3 3 15m backend-team
finance-team-operators replicaset.apps/moneymaking-operator-67f995d6f7 1 1 1 12m finance-team
kube-system replicaset.apps/coredns-76f75df574 2 2 2 41m ops-team
local-path-storage replicaset.apps/local-path-provisioner-7577fdbbfb 1 1 1 41m ops-team
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get clusterrole,clusterrolebinding --context wildcard
NAME CREATED AT
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
NAME ROLE AGE
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 69m
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 69m
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 69m
```
### Via the 'finance' context
```shell
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get namespaces --context finance
NAME STATUS AGE
finance-team-jobs Active 53m
finance-team-operators Active 53m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get all --context finance --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
finance-team-jobs pod/makemoney-job-28615152-vhgjx 1/1 Running 0 8s
finance-team-operators pod/moneymaking-operator-67f995d6f7-52zf4 1/1 Running 0 39m
NAMESPACE NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
finance-team-jobs cronjob.batch/makemoney-job */1 * * * * False 8 8s 38m
NAMESPACE NAME COMPLETIONS DURATION AGE
finance-team-jobs job.batch/makemoney-job-28615132 0/1 20m 20m
finance-team-jobs job.batch/makemoney-job-28615133 0/1 19m 19m
finance-team-jobs job.batch/makemoney-job-28615134 0/1 18m 18m
finance-team-jobs job.batch/makemoney-job-28615135 0/1 17m 17m
finance-team-jobs job.batch/makemoney-job-28615142 0/1 10m 10m
finance-team-jobs job.batch/makemoney-job-28615147 1/1 5m7s 5m8s
finance-team-jobs job.batch/makemoney-job-28615148 1/1 4m7s 4m8s
finance-team-jobs job.batch/makemoney-job-28615149 1/1 3m7s 3m8s
finance-team-jobs job.batch/makemoney-job-28615150 0/1 2m8s 2m8s
finance-team-jobs job.batch/makemoney-job-28615151 0/1 68s 68s
finance-team-jobs job.batch/makemoney-job-28615152 0/1 8s 8s
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
finance-team-operators deployment.apps/moneymaking-operator 1/1 1 1 39m
NAMESPACE NAME DESIRED CURRENT READY AGE
finance-team-operators replicaset.apps/moneymaking-operator-67f995d6f7 1 1 1 39m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get clusterrole,clusterrolebinding --context finance
NAME CREATED AT
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
NAME ROLE AGE
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 68m
```
### Via the 'backend' context
```shell
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get namespaces --context backend
NAME STATUS AGE
backend-development Active 26m
backend-searchservice Active 26m
backend-staging Active 26m
backend-webservices Active 26m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get all --context backend --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
backend-development pod/searchtool-dev-coolthing-f4d4b8f87-42vc7 1/1 Running 0 42m
backend-development pod/searchtool-dev-newfeature-d94bbb8d-b6hn7 1/1 Running 0 42m
backend-searchservice pod/mycool-search-tool-ccc89d7d4-f9zdz 1/1 Running 0 43m
backend-searchservice pod/mycool-search-tool-ccc89d7d4-p7xl6 1/1 Running 0 43m
backend-searchservice pod/mycool-search-tool-ccc89d7d4-s9czj 1/1 Running 0 43m
backend-staging pod/searchtool-staging-5f8c55bb8c-5cbzf 1/1 Running 0 42m
backend-webservices pod/webserver-667ddc69b6-94bss 1/1 Running 0 44m
backend-webservices pod/webserver-667ddc69b6-bh4t5 1/1 Running 0 44m
backend-webservices pod/webserver-667ddc69b6-hmqtm 1/1 Running 0 44m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
backend-development deployment.apps/searchtool-dev-coolthing 1/1 1 1 42m
backend-development deployment.apps/searchtool-dev-newfeature 1/1 1 1 42m
backend-searchservice deployment.apps/mycool-search-tool 3/3 3 3 43m
backend-staging deployment.apps/searchtool-staging 1/1 1 1 42m
backend-webservices deployment.apps/webserver 3/3 3 3 44m
NAMESPACE NAME DESIRED CURRENT READY AGE
backend-development replicaset.apps/searchtool-dev-coolthing-f4d4b8f87 1 1 1 42m
backend-development replicaset.apps/searchtool-dev-newfeature-d94bbb8d 1 1 1 42m
backend-searchservice replicaset.apps/mycool-search-tool-ccc89d7d4 3 3 3 43m
backend-staging replicaset.apps/searchtool-staging-5f8c55bb8c 1 1 1 42m
backend-webservices replicaset.apps/webserver-667ddc69b6 3 3 3 44m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get clusterrole,clusterrolebinding --context backend
NAME CREATED AT
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
NAME ROLE AGE
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 68m
```
### Via the 'ops' context
```shell
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get namespaces --context ops
NAME STATUS AGE
kube-node-lease Active 65m
kube-public Active 65m
kube-system Active 65m
local-path-storage Active 65m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get all --context ops --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system pod/coredns-76f75df574-2gnfj 1/1 Running 1 (65m ago) 70m
kube-system pod/coredns-76f75df574-8rq7f 1/1 Running 1 (65m ago) 70m
kube-system pod/etcd-kind-control-plane 1/1 Running 0 65m
kube-system pod/kindnet-59vvq 1/1 Running 1 (65m ago) 70m
kube-system pod/kindnet-zb5kv 1/1 Running 1 (65m ago) 70m
kube-system pod/kube-apiserver-kind-control-plane 1/1 Running 0 65m
kube-system pod/kube-controller-manager-kind-control-plane 1/1 Running 1 (65m ago) 70m
kube-system pod/kube-proxy-9q5ps 1/1 Running 1 (65m ago) 70m
kube-system pod/kube-proxy-ljthv 1/1 Running 1 (65m ago) 70m
kube-system pod/kube-scheduler-kind-control-plane 1/1 Running 1 (65m ago) 70m
local-path-storage pod/local-path-provisioner-7577fdbbfb-5jzd4 1/1 Running 2 (64m ago) 70m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 70m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system daemonset.apps/kindnet 2 2 2 2 2 kubernetes.io/os=linux 70m
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 70m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/coredns 2/2 2 2 70m
local-path-storage deployment.apps/local-path-provisioner 1/1 1 1 70m
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/coredns-76f75df574 2 2 2 70m
local-path-storage replicaset.apps/local-path-provisioner-7577fdbbfb 1 1 1 70m
(⎈|kind-kind:N/A)james@Jamess-MacBook-Pro kcp % kubectl get clusterrole,clusterrolebinding --context ops
NAME CREATED AT
clusterrole.rbac.authorization.k8s.io/workspace-admin 2024-05-28T14:59:54Z
NAME ROLE AGE
clusterrolebinding.rbac.authorization.k8s.io/workspace-admin ClusterRole/workspace-admin 68m
```