Skip to content

Commit 1110e7d

Browse files
author
blink-so
committed
feat: add support for watching pods across multiple namespaces
- When namespace is empty, watch all namespaces using ClusterRole/ClusterRoleBinding - When namespace is specified, watch only that namespace using Role/RoleBinding - Maintains backward compatibility with existing single-namespace deployments - Updates Helm chart to conditionally create appropriate RBAC resources - Updates documentation to explain multi-namespace support Fixes #5
1 parent 3b12d12 commit 1110e7d

File tree

8 files changed

+992
-148
lines changed

8 files changed

+992
-148
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Stream Kubernetes Pod events to the Coder startup logs.
1010
- Easily determine the reason for a pod provision failure, or why a pod is stuck in a pending state.
1111
- Visibility into when pods are OOMKilled, or when they are evicted.
1212
- Filter by namespace, field selector, and label selector to reduce Kubernetes API load.
13+
- Support for watching pods across multiple namespaces or all namespaces.
1314

1415
![Log Stream](./scripts/demo.png)
1516

@@ -24,6 +25,27 @@ helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
2425
--set url=<your-coder-url-including-http-or-https>
2526
```
2627

28+
### Multi-Namespace Support
29+
30+
By default, `coder-logstream-kube` will watch pods in all namespaces. This is useful for deployments where workspaces are spread across multiple namespaces (e.g., per-user namespaces).
31+
32+
To watch all namespaces (default behavior):
33+
```console
34+
helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
35+
--namespace coder \
36+
--set url=<your-coder-url>
37+
```
38+
39+
To watch a specific namespace only:
40+
```console
41+
helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
42+
--namespace coder \
43+
--set url=<your-coder-url> \
44+
--set namespace=<target-namespace>
45+
```
46+
47+
**Important**: When watching all namespaces, the Helm chart will create a `ClusterRole` and `ClusterRoleBinding` to provide the necessary cluster-wide permissions. When watching a specific namespace, it will create a `Role` and `RoleBinding` scoped to that namespace.
48+
2749
> **Note**
2850
> For additional customization (such as customizing the image, pull secrets, annotations, etc.), you can use the
2951
> [values.yaml](helm/values.yaml) file directly.
@@ -46,6 +68,8 @@ Kubernetes provides an [informers](https://pkg.go.dev/k8s.io/client-go/informers
4668

4769
`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.
4870

71+
When no namespace is specified (or the `CODER_NAMESPACE` environment variable is empty), the informers will watch all namespaces in the cluster. When a specific namespace is provided, the informers are scoped to that namespace only.
72+
4973
## Custom Certificates
5074

5175
- [`SSL_CERT_FILE`](https://go.dev/src/crypto/x509/root_unix.go#L19): Specifies the path to an SSL certificate.

README.md.backup

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: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- if .Values.namespace }}
12
apiVersion: rbac.authorization.k8s.io/v1
23
kind: Role
34
metadata:
@@ -10,13 +11,6 @@ rules:
1011
resources: ["replicasets", "events"]
1112
verbs: ["get", "watch", "list"]
1213
---
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-
---
2014
apiVersion: rbac.authorization.k8s.io/v1
2115
kind: RoleBinding
2216
metadata:
@@ -28,6 +22,39 @@ roleRef:
2822
subjects:
2923
- kind: ServiceAccount
3024
name: {{ .Values.serviceAccount.name | quote }}
25+
{{- else }}
26+
apiVersion: rbac.authorization.k8s.io/v1
27+
kind: ClusterRole
28+
metadata:
29+
name: coder-logstream-kube-clusterrole
30+
rules:
31+
- apiGroups: [""]
32+
resources: ["pods", "events"]
33+
verbs: ["get", "watch", "list"]
34+
- apiGroups: ["apps"]
35+
resources: ["replicasets", "events"]
36+
verbs: ["get", "watch", "list"]
37+
---
38+
apiVersion: rbac.authorization.k8s.io/v1
39+
kind: ClusterRoleBinding
40+
metadata:
41+
name: coder-logstream-kube-clusterrolebinding
42+
roleRef:
43+
apiGroup: rbac.authorization.k8s.io
44+
kind: ClusterRole
45+
name: coder-logstream-kube-clusterrole
46+
subjects:
47+
- kind: ServiceAccount
48+
name: {{ .Values.serviceAccount.name | quote }}
49+
namespace: {{ .Release.Namespace }}
50+
{{- end }}
51+
---
52+
apiVersion: v1
53+
kind: ServiceAccount
54+
metadata:
55+
name: {{ .Values.serviceAccount.name | quote }}
56+
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
57+
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
3158
---
3259
apiVersion: apps/v1
3360
kind: Deployment
@@ -75,8 +102,10 @@ spec:
75102
env:
76103
- name: CODER_URL
77104
value: {{ .Values.url }}
105+
{{- if .Values.namespace }}
78106
- name: CODER_NAMESPACE
79-
value: {{ .Values.namespace | default .Release.Namespace }}
107+
value: {{ .Values.namespace }}
108+
{{- end }}
80109
{{- if .Values.image.sslCertFile }}
81110
- name: SSL_CERT_FILE
82111
value: {{ .Values.image.sslCertFile }}
@@ -95,3 +124,4 @@ spec:
95124
{{- if .Values.volumes }}
96125
volumes: {{- toYaml .Values.volumes | nindent 8 }}
97126
{{- end }}
127+

helm/templates/service.yaml.backup

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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
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.
5-
# If unspecified, this defaults to the Helm namespace.
3+
# namespace -- The namespace to search for Pods within.
4+
# If unspecified or empty, coder-logstream-kube will watch pods in all namespaces.
5+
# When watching all namespaces, ClusterRole and ClusterRoleBinding will be created
6+
# instead of Role and RoleBinding to provide the necessary permissions.
7+
# If specified, only pods in that namespace will be watched and Role/RoleBinding
8+
# will be used for namespace-scoped permissions.
69
namespace: ""
710

811
# volumes -- A list of extra volumes to add to the coder-logstream pod.
@@ -101,3 +104,4 @@ securityContext: {}
101104
# runAsNonRoot: true
102105
# seccompProfile:
103106
# type: RuntimeDefault
107+

helm/values.yaml.backup

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# url -- The URL of your Coder deployment. Must prefix with http or https
2+
url: ""
3+
4+
# namespace -- The namespace to searching for Pods within.
5+
# If unspecified, this defaults to the Helm namespace.
6+
namespace: ""
7+
8+
# volumes -- A list of extra volumes to add to the coder-logstream pod.
9+
volumes:
10+
# emptyDir: {}
11+
# - name: "my-volume"
12+
13+
# volumeMounts -- A list of extra volume mounts to add to the coder-logstream pod.
14+
volumeMounts:
15+
# - name: "my-volume"
16+
# mountPath: "/mnt/my-volume"
17+
18+
# image -- The image to use.
19+
image:
20+
# image.repo -- The repository of the image.
21+
repo: "ghcr.io/coder/coder-logstream-kube"
22+
# image.tag -- The tag of the image, defaults to {{.Chart.AppVersion}}
23+
# if not set. If you're using the chart directly from git, the default
24+
# app version will not work and you'll need to set this value. The helm
25+
# chart helpfully fails quickly in this case.
26+
tag: ""
27+
# image.pullPolicy -- The pull policy to use for the image. See:
28+
# https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy
29+
pullPolicy: IfNotPresent
30+
# image.pullSecrets -- The secrets used for pulling the Coder image from
31+
# a private registry.
32+
pullSecrets: []
33+
# - name: "pull-secret"
34+
# image.sslCertFile -- Location of the SSL certificate file. Sets the $SSL_CERT_FILE
35+
# variable inside of the container.
36+
sslCertFile: ""
37+
# image.sslCertDir -- Directory to check for SSL certificate files. Sets the $SSL_CERT_DIR
38+
# variable inside of the container.
39+
sslCertDir: ""
40+
41+
serviceAccount:
42+
# serviceAccount.annotations -- The service account annotations.
43+
annotations: {}
44+
# serviceAccount.labels -- The service account labels.
45+
labels: {}
46+
# coder.serviceAccount.name -- The service account name
47+
name: coder-logstream-kube
48+
49+
# resources -- The resources to request for the Deployment. These are optional
50+
# and are not set by default.
51+
resources:
52+
{}
53+
# limits:
54+
# cpu: 500m
55+
# memory: 500Mi
56+
# requests:
57+
# cpu: 2000m
58+
# memory: 2000Mi
59+
60+
# nodeSelector -- Node labels for constraining the coder-logstream pod to specific nodes.
61+
nodeSelector: {}
62+
63+
# affinity -- Allows specifying an affinity rule for the Deployment.
64+
# The default rule prefers to schedule coder pods on different
65+
# nodes, which is only applicable if coder.replicaCount is greater than 1.
66+
affinity:
67+
{}
68+
# podAntiAffinity:
69+
# preferredDuringSchedulingIgnoredDuringExecution:
70+
# - podAffinityTerm:
71+
# labelSelector:
72+
# matchExpressions:
73+
# - key: app.kubernetes.io/instance: coder-logstream-kube
74+
# operator: In
75+
# values:
76+
# - "true"
77+
# topologyKey: kubernetes.io/hostname
78+
# weight: 1
79+
80+
# tolerations -- Tolerations for tainted nodes.
81+
# See: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
82+
tolerations:
83+
{}
84+
# - key: "key"
85+
# operator: "Equal"
86+
# value: "value"
87+
# effect: "NoSchedule"
88+
89+
# labels -- The pod labels for coder-logstream-kube. See:
90+
# https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
91+
labels: {}
92+
93+
# securityContext -- Container-level security context
94+
# See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
95+
securityContext: {}
96+
# allowPrivilegeEscalation: false
97+
# capabilities:
98+
# drop:
99+
# - ALL
100+
# readOnlyRootFilesystem: true
101+
# runAsNonRoot: true
102+
# seccompProfile:
103+
# type: RuntimeDefault

0 commit comments

Comments
 (0)