Skip to content

Commit d2d6be2

Browse files
committed
Add support for multiple namespaces
- Allow CODER_NAMESPACE to accept comma-separated list of namespaces - Support watching all namespaces when CODER_NAMESPACE is empty - Automatically use ClusterRole/ClusterRoleBinding for multi-namespace or all-namespace scenarios - Update Helm chart to support both namespace-scoped and cluster-wide RBAC - Add comprehensive documentation for multi-namespace usage - Maintain backward compatibility with single namespace deployments Fixes #5
1 parent 3b12d12 commit d2d6be2

File tree

11 files changed

+1326
-245
lines changed

11 files changed

+1326
-245
lines changed

README.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
# coder-logstream-kube
22

3-
[![discord](https://img.shields.io/discord/747933592273027093?label=discord)](https://discord.gg/coder)
4-
[![release](https://img.shields.io/github/v/tag/coder/coder-logstream-kube)](https://github.com/coder/envbuilder/pkgs/container/coder-logstream-kube)
5-
[![godoc](https://pkg.go.dev/badge/github.com/coder/coder-logstream-kube.svg)](https://pkg.go.dev/github.com/coder/coder-logstream-kube)
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/coder/coder-logstream-kube.svg)](https://pkg.go.dev/github.com/coder/coder-logstream-kube)
64
[![license](https://img.shields.io/github/license/coder/coder-logstream-kube)](./LICENSE)
75

86
Stream Kubernetes Pod events to the Coder startup logs.
97

108
- Easily determine the reason for a pod provision failure, or why a pod is stuck in a pending state.
119
- Visibility into when pods are OOMKilled, or when they are evicted.
1210
- Filter by namespace, field selector, and label selector to reduce Kubernetes API load.
11+
- Support for watching multiple namespaces or all namespaces cluster-wide.
1312

1413
![Log Stream](./scripts/demo.png)
1514

@@ -24,6 +23,36 @@ helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
2423
--set url=<your-coder-url-including-http-or-https>
2524
```
2625

26+
### Multi-Namespace Support
27+
28+
By default, `coder-logstream-kube` watches pods in the namespace where it's deployed. You can configure it to watch multiple namespaces or all namespaces:
29+
30+
#### Watch specific namespaces
31+
```console
32+
helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
33+
--namespace coder \
34+
--set url=<your-coder-url> \
35+
--set namespaces="namespace1,namespace2,namespace3"
36+
```
37+
38+
#### Watch all namespaces
39+
```console
40+
helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
41+
--namespace coder \
42+
--set url=<your-coder-url> \
43+
--set namespaces=""
44+
```
45+
46+
When watching multiple namespaces or all namespaces, the chart automatically creates ClusterRole and ClusterRoleBinding resources instead of namespace-scoped Role and RoleBinding.
47+
48+
### Environment Variable Configuration
49+
50+
You can also configure namespaces using the `CODER_NAMESPACE` environment variable:
51+
52+
- Single namespace: `CODER_NAMESPACE=my-namespace`
53+
- Multiple namespaces: `CODER_NAMESPACE=ns1,ns2,ns3`
54+
- All namespaces: `CODER_NAMESPACE=""` (empty string)
55+
2756
> **Note**
2857
> For additional customization (such as customizing the image, pull secrets, annotations, etc.), you can use the
2958
> [values.yaml](helm/values.yaml) file directly.
@@ -46,7 +75,24 @@ Kubernetes provides an [informers](https://pkg.go.dev/k8s.io/client-go/informers
4675

4776
`coder-logstream-kube` listens for pod creation events with containers that have the `CODER_AGENT_TOKEN` environment variable set. All pod events are streamed as logs to the Coder API using the agent token for authentication.
4877

78+
When configured for multiple namespaces, the application creates separate informers for each specified namespace. When configured to watch all namespaces (empty namespace list), it uses cluster-wide informers.
79+
4980
## Custom Certificates
5081

5182
- [`SSL_CERT_FILE`](https://go.dev/src/crypto/x509/root_unix.go#L19): Specifies the path to an SSL certificate.
5283
- [`SSL_CERT_DIR`](https://go.dev/src/crypto/x509/root_unix.go#L25): Identifies which directory to check for SSL certificate files.
84+
85+
## RBAC Permissions
86+
87+
The required permissions depend on the scope of namespaces being watched:
88+
89+
### Single Namespace (Role/RoleBinding)
90+
When watching a single namespace, the application uses namespace-scoped permissions:
91+
- `pods`: get, watch, list
92+
- `events`: get, watch, list
93+
- `replicasets`: get, watch, list
94+
95+
### Multiple Namespaces or All Namespaces (ClusterRole/ClusterRoleBinding)
96+
When watching multiple namespaces or all namespaces, the application requires cluster-wide permissions with the same resource access but across all namespaces.
97+
98+
The Helm chart automatically determines which type of RBAC resources to create based on your configuration.

README.md.bak

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# coder-logstream-kube
2+
3+
[![discord](https://img.shields.io/discord/747933592273027093?label=discord)](https://discord.gg/coder)
4+
[![release](https://img.shields.io/github/v/tag/coder/coder-logstream-kube)](https://github.com/coder/envbuilder/pkgs/container/coder-logstream-kube)
5+
[![godoc](https://pkg.go.dev/badge/github.com/coder/coder-logstream-kube.svg)](https://pkg.go.dev/github.com/coder/coder-logstream-kube)
6+
[![license](https://img.shields.io/github/license/coder/coder-logstream-kube)](./LICENSE)
7+
8+
Stream Kubernetes Pod events to the Coder startup logs.
9+
10+
- Easily determine the reason for a pod provision failure, or why a pod is stuck in a pending state.
11+
- Visibility into when pods are OOMKilled, or when they are evicted.
12+
- Filter by namespace, field selector, and label selector to reduce Kubernetes API load.
13+
14+
![Log Stream](./scripts/demo.png)
15+
16+
## Usage
17+
18+
Apply the Helm chart to start streaming logs into your Coder instance:
19+
20+
```console
21+
helm repo add coder-logstream-kube https://helm.coder.com/logstream-kube
22+
helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
23+
--namespace coder \
24+
--set url=<your-coder-url-including-http-or-https>
25+
```
26+
27+
> **Note**
28+
> For additional customization (such as customizing the image, pull secrets, annotations, etc.), you can use the
29+
> [values.yaml](helm/values.yaml) file directly.
30+
31+
Your Coder template should be using a `kubernetes_deployment` resource with `wait_for_rollout` set to `false`.
32+
33+
```hcl
34+
resource "kubernetes_deployment" "hello_world" {
35+
count = data.coder_workspace.me.start_count
36+
wait_for_rollout = false
37+
...
38+
}
39+
```
40+
41+
This ensures all pod events will be sent during initialization and startup.
42+
43+
## How?
44+
45+
Kubernetes provides an [informers](https://pkg.go.dev/k8s.io/client-go/informers) API that streams pod and event data from the API server.
46+
47+
`coder-logstream-kube` listens for pod creation events with containers that have the `CODER_AGENT_TOKEN` environment variable set. All pod events are streamed as logs to the Coder API using the agent token for authentication.
48+
49+
## Custom Certificates
50+
51+
- [`SSL_CERT_FILE`](https://go.dev/src/crypto/x509/root_unix.go#L19): Specifies the path to an SSL certificate.
52+
- [`SSL_CERT_DIR`](https://go.dev/src/crypto/x509/root_unix.go#L25): Identifies which directory to check for SSL certificate files.

helm/templates/service.yaml

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1+
{{/*
2+
Determine if cluster-wide permissions are needed.
3+
This happens when:
4+
1. namespaces is explicitly set to empty string (watch all namespaces)
5+
2. namespaces contains multiple comma-separated values
6+
3. rbac.clusterWide is explicitly set to true
7+
*/}}
8+
{{- $namespaces := .Values.namespaces | default .Release.Namespace -}}
9+
{{- $namespacesCount := 0 -}}
10+
{{- if eq $namespaces "" -}}
11+
{{- $namespacesCount = 0 -}}
12+
{{- else -}}
13+
{{- $namespacesCount = len (splitList "," $namespaces) -}}
14+
{{- end -}}
15+
{{- $useClusterWide := or .Values.rbac.clusterWide (eq $namespaces "") (gt $namespacesCount 1) -}}
16+
17+
{{- if $useClusterWide }}
118
apiVersion: rbac.authorization.k8s.io/v1
2-
kind: Role
19+
kind: ClusterRole
320
metadata:
4-
name: coder-logstream-kube-role
21+
name: {{ .Release.Name }}-coder-logstream-kube-role
522
rules:
623
- apiGroups: [""]
724
resources: ["pods", "events"]
@@ -10,12 +27,30 @@ rules:
1027
resources: ["replicasets", "events"]
1128
verbs: ["get", "watch", "list"]
1229
---
13-
apiVersion: v1
14-
kind: ServiceAccount
30+
apiVersion: rbac.authorization.k8s.io/v1
31+
kind: ClusterRoleBinding
1532
metadata:
33+
name: {{ .Release.Name }}-coder-logstream-kube-rolebinding
34+
roleRef:
35+
apiGroup: rbac.authorization.k8s.io
36+
kind: ClusterRole
37+
name: {{ .Release.Name }}-coder-logstream-kube-role
38+
subjects:
39+
- kind: ServiceAccount
1640
name: {{ .Values.serviceAccount.name | quote }}
17-
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
18-
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
41+
namespace: {{ .Release.Namespace }}
42+
{{- else }}
43+
apiVersion: rbac.authorization.k8s.io/v1
44+
kind: Role
45+
metadata:
46+
name: coder-logstream-kube-role
47+
rules:
48+
- apiGroups: [""]
49+
resources: ["pods", "events"]
50+
verbs: ["get", "watch", "list"]
51+
- apiGroups: ["apps"]
52+
resources: ["replicasets", "events"]
53+
verbs: ["get", "watch", "list"]
1954
---
2055
apiVersion: rbac.authorization.k8s.io/v1
2156
kind: RoleBinding
@@ -28,6 +63,14 @@ roleRef:
2863
subjects:
2964
- kind: ServiceAccount
3065
name: {{ .Values.serviceAccount.name | quote }}
66+
{{- end }}
67+
---
68+
apiVersion: v1
69+
kind: ServiceAccount
70+
metadata:
71+
name: {{ .Values.serviceAccount.name | quote }}
72+
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
73+
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
3174
---
3275
apiVersion: apps/v1
3376
kind: Deployment
@@ -76,7 +119,7 @@ spec:
76119
- name: CODER_URL
77120
value: {{ .Values.url }}
78121
- name: CODER_NAMESPACE
79-
value: {{ .Values.namespace | default .Release.Namespace }}
122+
value: {{ $namespaces }}
80123
{{- if .Values.image.sslCertFile }}
81124
- name: SSL_CERT_FILE
82125
value: {{ .Values.image.sslCertFile }}
@@ -95,3 +138,4 @@ spec:
95138
{{- if .Values.volumes }}
96139
volumes: {{- toYaml .Values.volumes | nindent 8 }}
97140
{{- end }}
141+

helm/templates/service.yaml.bak

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: Role
3+
metadata:
4+
name: coder-logstream-kube-role
5+
rules:
6+
- apiGroups: [""]
7+
resources: ["pods", "events"]
8+
verbs: ["get", "watch", "list"]
9+
- apiGroups: ["apps"]
10+
resources: ["replicasets", "events"]
11+
verbs: ["get", "watch", "list"]
12+
---
13+
apiVersion: v1
14+
kind: ServiceAccount
15+
metadata:
16+
name: {{ .Values.serviceAccount.name | quote }}
17+
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
18+
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
19+
---
20+
apiVersion: rbac.authorization.k8s.io/v1
21+
kind: RoleBinding
22+
metadata:
23+
name: coder-logstream-kube-rolebinding
24+
roleRef:
25+
apiGroup: rbac.authorization.k8s.io
26+
kind: Role
27+
name: coder-logstream-kube-role
28+
subjects:
29+
- kind: ServiceAccount
30+
name: {{ .Values.serviceAccount.name | quote }}
31+
---
32+
apiVersion: apps/v1
33+
kind: Deployment
34+
metadata:
35+
name: coder-logstream-kube
36+
spec:
37+
# This must remain at 1 otherwise duplicate logs can occur!
38+
replicas: 1
39+
selector:
40+
matchLabels:
41+
app.kubernetes.io/instance: {{ .Release.Name }}
42+
template:
43+
metadata:
44+
labels:
45+
app.kubernetes.io/instance: {{ .Release.Name }}
46+
{{- with .Values.labels }}
47+
{{- toYaml . | nindent 8 }}
48+
{{- end }}
49+
spec:
50+
serviceAccountName: {{ .Values.serviceAccount.name | quote }}
51+
restartPolicy: Always
52+
{{- with .Values.image.pullSecrets }}
53+
imagePullSecrets:
54+
{{- toYaml . | nindent 8 }}
55+
{{- end }}
56+
{{- with .Values.affinity }}
57+
affinity:
58+
{{- toYaml . | nindent 8 }}
59+
{{- end }}
60+
{{- with .Values.tolerations }}
61+
tolerations:
62+
{{- toYaml . | nindent 8 }}
63+
{{- end }}
64+
{{- with .Values.nodeSelector }}
65+
nodeSelector:
66+
{{- toYaml . | nindent 8 }}
67+
{{- end }}
68+
containers:
69+
- name: coder-logstream-kube
70+
image: "{{ .Values.image.repo }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
71+
imagePullPolicy: {{ .Values.image.pullPolicy }}
72+
command:
73+
- /coder-logstream-kube
74+
resources: {{ toYaml .Values.resources | nindent 12 }}
75+
env:
76+
- name: CODER_URL
77+
value: {{ .Values.url }}
78+
- name: CODER_NAMESPACE
79+
value: {{ .Values.namespace | default .Release.Namespace }}
80+
{{- if .Values.image.sslCertFile }}
81+
- name: SSL_CERT_FILE
82+
value: {{ .Values.image.sslCertFile }}
83+
{{- end }}
84+
{{- if .Values.image.sslCertDir }}
85+
- name: SSL_CERT_DIR
86+
value: {{ .Values.image.sslCertDir }}
87+
{{- end }}
88+
{{- with .Values.securityContext }}
89+
securityContext:
90+
{{- toYaml . | nindent 12 }}
91+
{{- end }}
92+
{{- if .Values.volumeMounts }}
93+
volumeMounts: {{- toYaml .Values.volumeMounts | nindent 12 }}
94+
{{- end }}
95+
{{- if .Values.volumes }}
96+
volumes: {{- toYaml .Values.volumes | nindent 8 }}
97+
{{- end }}

helm/values.yaml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# url -- The URL of your Coder deployment. Must prefix with http or https
21
url: ""
32

4-
# namespace -- The namespace to searching for Pods within.
3+
# namespaces -- Comma-separated list of namespaces to watch for Pods.
54
# If unspecified, this defaults to the Helm namespace.
6-
namespace: ""
5+
# If set to empty string (""), watches all namespaces (requires cluster-wide permissions).
6+
namespaces: ""
77

88
# volumes -- A list of extra volumes to add to the coder-logstream pod.
99
volumes:
@@ -46,6 +46,12 @@ serviceAccount:
4646
# coder.serviceAccount.name -- The service account name
4747
name: coder-logstream-kube
4848

49+
# rbac -- RBAC configuration
50+
rbac:
51+
# rbac.clusterWide -- Whether to use cluster-wide permissions (ClusterRole/ClusterRoleBinding).
52+
# This is automatically set to true when namespaces is empty or contains multiple namespaces.
53+
clusterWide: false
54+
4955
# resources -- The resources to request for the Deployment. These are optional
5056
# and are not set by default.
5157
resources:
@@ -98,6 +104,3 @@ securityContext: {}
98104
# drop:
99105
# - ALL
100106
# readOnlyRootFilesystem: true
101-
# runAsNonRoot: true
102-
# seccompProfile:
103-
# type: RuntimeDefault

0 commit comments

Comments
 (0)