Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbca9af2

Browse files
authored
feat: add multi-namespace support (#124)
* feat: support watching multiple namespaces with cluster-wide RBAC* Update README* Apply review suggestions
1 parentb45e9d5 commitbca9af2

File tree

6 files changed

+244
-27
lines changed

6 files changed

+244
-27
lines changed

‎README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
2424
--set url=<your-coder-url-including-http-or-https>
2525
```
2626

27+
>**Multi-Namespace support**
28+
>
29+
>By default, coder-logstream-kube will watch all namespaces in the cluster. To limit which namespaces are monitored, you can specify them in the[values.yaml](helm/values.yaml) file:
30+
>
31+
>```yaml
32+
># Watch specific namespaces only
33+
> namespaces: ["default", "kube-system"]
34+
>
35+
># Watch all namespaces (default)
36+
> namespaces: []
37+
> ```
38+
>
39+
> When `namespaces` is empty or not specified, the service will monitor all namespaces in the cluster.
40+
2741
> **Note**
2842
> For additional customization (such as customizing the image, pull secrets, annotations, etc.), you can use the
2943
> [values.yaml](helm/values.yaml) file directly.

‎helm/templates/service.yaml‎

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
1-
apiVersion:rbac.authorization.k8s.io/v1
2-
kind:Role
3-
metadata:
4-
name:coder-logstream-kube-role
5-
rules:
1+
{{- define "coder-logstream-kube.rules" -}}
62
-apiGroups:[""]
73
resources:["pods", "events"]
84
verbs:["get", "watch", "list"]
95
-apiGroups:["apps"]
106
resources:["replicasets", "events"]
117
verbs:["get", "watch", "list"]
8+
{{- end -}}
9+
10+
{{- if .Values.namespaces }}
11+
{{- range .Values.namespaces }}
1212
---
13-
apiVersion:v1
14-
kind:ServiceAccount
13+
apiVersion:rbac.authorization.k8s.io/v1
14+
kind:Role
1515
metadata:
16-
name:{{ .Values.serviceAccount.name | quote }}
17-
annotations:{{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
18-
labels:{{ toYaml .Values.serviceAccount.labels | nindent 4 }}
16+
name:coder-logstream-kube-role
17+
namespace:{{ . }}
18+
rules:
19+
{{ include "coder-logstream-kube.rules" . | nindent 2 }}
1920
---
2021
apiVersion:rbac.authorization.k8s.io/v1
2122
kind:RoleBinding
2223
metadata:
2324
name:coder-logstream-kube-rolebinding
25+
namespace:{{ . }}
2426
roleRef:
2527
apiGroup:rbac.authorization.k8s.io
2628
kind:Role
2729
name:coder-logstream-kube-role
2830
subjects:
31+
-kind:ServiceAccount
32+
name:{{ $.Values.serviceAccount.name | quote }}
33+
namespace:{{ $.Release.Namespace }}
34+
{{- end }}
35+
{{- else }}
36+
---
37+
apiVersion:rbac.authorization.k8s.io/v1
38+
kind:ClusterRole
39+
metadata:
40+
name:coder-logstream-kube-role
41+
rules:
42+
{{ include "coder-logstream-kube.rules" . | nindent 2 }}
43+
---
44+
apiVersion:rbac.authorization.k8s.io/v1
45+
kind:ClusterRoleBinding
46+
metadata:
47+
name:coder-logstream-kube-rolebinding
48+
roleRef:
49+
apiGroup:rbac.authorization.k8s.io
50+
kind:ClusterRole
51+
name:coder-logstream-kube-role
52+
subjects:
2953
-kind:ServiceAccount
3054
name:{{ .Values.serviceAccount.name | quote }}
55+
namespace:{{ .Release.Namespace }}
56+
{{- end }}
57+
---
58+
apiVersion:v1
59+
kind:ServiceAccount
60+
metadata:
61+
name:{{ .Values.serviceAccount.name | quote }}
62+
annotations:{{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
63+
labels:{{ toYaml .Values.serviceAccount.labels | nindent 4 }}
3164
---
3265
apiVersion:apps/v1
3366
kind:Deployment
@@ -75,8 +108,10 @@ spec:
75108
env:
76109
-name:CODER_URL
77110
value:{{ .Values.url }}
78-
-name:CODER_NAMESPACE
79-
value:{{ .Values.namespace | default .Release.Namespace }}
111+
{{- if .Values.namespaces }}
112+
-name:CODER_NAMESPACES
113+
value:{{ join "," .Values.namespaces }}
114+
{{- end }}
80115
{{- if .Values.image.sslCertFile }}
81116
-name:SSL_CERT_FILE
82117
value:{{ .Values.image.sslCertFile }}

‎helm/values.yaml‎

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

4-
# namespace --The namespacetosearching for Pods within.
5-
# If unspecified, this defaults to the Helm namespace.
6-
namespace:""
4+
# namespace --List of namespacestosearch for Pods within.
5+
# If unspecified or empty it will watch all namespaces.
6+
namespaces:[]
77

88
# volumes -- A list of extra volumes to add to the coder-logstream pod.
99
volumes:

‎logger.go‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type podEventLoggerOptions struct {
3636
logDebounce time.Duration
3737

3838
// The following fields are optional!
39-
namespacestring
39+
namespaces[]string
4040
fieldSelectorstring
4141
labelSelectorstring
4242
}
@@ -78,7 +78,18 @@ func newPodEventLogger(ctx context.Context, opts podEventLoggerOptions) (*podEve
7878
},
7979
}
8080

81-
returnreporter,reporter.init()
81+
// If no namespaces are provided, we listen for events in all namespaces.
82+
iflen(opts.namespaces)==0 {
83+
reporter.initNamespace("")
84+
}else {
85+
for_,namespace:=rangeopts.namespaces {
86+
iferr:=reporter.initNamespace(namespace);err!=nil {
87+
returnnil,err
88+
}
89+
}
90+
}
91+
92+
returnreporter,nil
8293
}
8394

8495
typepodEventLoggerstruct {
@@ -95,22 +106,23 @@ type podEventLogger struct {
95106
lq*logQueuer
96107
}
97108

98-
// init starts the informer factory and registers event handlers.
99-
func (p*podEventLogger)init()error {
109+
// initNamespace starts the informer factory and registers event handlers for a given namespace.
110+
// If provided namespace is empty, it will start the informer factory and register event handlers for all namespaces.
111+
func (p*podEventLogger)initNamespace(namespacestring)error {
100112
// We only track events that happen after the reporter starts.
101113
// This is to prevent us from sending duplicate events.
102114
startTime:=time.Now()
103115

104116
gop.lq.work(p.ctx)
105117

106-
podFactory:=informers.NewSharedInformerFactoryWithOptions(p.client,0,informers.WithNamespace(p.namespace),informers.WithTweakListOptions(func(lo*v1.ListOptions) {
118+
podFactory:=informers.NewSharedInformerFactoryWithOptions(p.client,0,informers.WithNamespace(namespace),informers.WithTweakListOptions(func(lo*v1.ListOptions) {
107119
lo.FieldSelector=p.fieldSelector
108120
lo.LabelSelector=p.labelSelector
109121
}))
110122
eventFactory:=podFactory
111123
ifp.fieldSelector!=""||p.labelSelector!="" {
112124
// Events cannot filter on labels and fields!
113-
eventFactory=informers.NewSharedInformerFactoryWithOptions(p.client,0,informers.WithNamespace(p.namespace))
125+
eventFactory=informers.NewSharedInformerFactoryWithOptions(p.client,0,informers.WithNamespace(namespace))
114126
}
115127

116128
// We listen for Pods and Events in the informer factory.
@@ -277,7 +289,7 @@ func (p *podEventLogger) init() error {
277289

278290
p.logger.Info(p.ctx,"listening for pod events",
279291
slog.F("coder_url",p.coderURL.String()),
280-
slog.F("namespace",p.namespace),
292+
slog.F("namespace",namespace),
281293
slog.F("field_selector",p.fieldSelector),
282294
slog.F("label_selector",p.labelSelector),
283295
)

‎logger_test.go‎

Lines changed: 149 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestReplicaSetEvents(t *testing.T) {
4747
reporter,err:=newPodEventLogger(ctx,podEventLoggerOptions{
4848
client:client,
4949
coderURL:agentURL,
50-
namespace:namespace,
50+
namespaces:[]string{namespace},
5151
logger:slogtest.Make(t,nil).Leveled(slog.LevelDebug),
5252
logDebounce:5*time.Second,
5353
clock:cMock,
@@ -144,7 +144,7 @@ func TestPodEvents(t *testing.T) {
144144
reporter,err:=newPodEventLogger(ctx,podEventLoggerOptions{
145145
client:client,
146146
coderURL:agentURL,
147-
namespace:namespace,
147+
namespaces:[]string{namespace},
148148
logger:slogtest.Make(t,nil).Leveled(slog.LevelDebug),
149149
logDebounce:5*time.Second,
150150
clock:cMock,
@@ -221,6 +221,153 @@ func TestPodEvents(t *testing.T) {
221221
require.NoError(t,err)
222222
}
223223

224+
funcTest_newPodEventLogger_multipleNamespaces(t*testing.T) {
225+
t.Parallel()
226+
227+
api:=newFakeAgentAPI(t)
228+
229+
ctx:=testutil.Context(t,testutil.WaitShort)
230+
agentURL,err:=url.Parse(api.server.URL)
231+
require.NoError(t,err)
232+
namespaces:= []string{"test-namespace1","test-namespace2"}
233+
client:=fake.NewSimpleClientset()
234+
235+
cMock:=quartz.NewMock(t)
236+
reporter,err:=newPodEventLogger(ctx,podEventLoggerOptions{
237+
client:client,
238+
coderURL:agentURL,
239+
namespaces:namespaces,
240+
logger:slogtest.Make(t,nil).Leveled(slog.LevelDebug),
241+
logDebounce:5*time.Second,
242+
clock:cMock,
243+
})
244+
require.NoError(t,err)
245+
246+
// Create a pod in the test-namespace1 namespace
247+
pod1:=&corev1.Pod{
248+
ObjectMeta: v1.ObjectMeta{
249+
Name:"test-pod-1",
250+
Namespace:"test-namespace1",
251+
CreationTimestamp: v1.Time{
252+
Time:time.Now().Add(time.Hour),
253+
},
254+
},
255+
Spec: corev1.PodSpec{
256+
Containers: []corev1.Container{
257+
{
258+
Env: []corev1.EnvVar{
259+
{
260+
Name:"CODER_AGENT_TOKEN",
261+
Value:"test-token-1",
262+
},
263+
},
264+
},
265+
},
266+
},
267+
}
268+
_,err=client.CoreV1().Pods("test-namespace1").Create(ctx,pod1, v1.CreateOptions{})
269+
require.NoError(t,err)
270+
271+
// Create a pod in the test-namespace2 namespace
272+
pod2:=&corev1.Pod{
273+
ObjectMeta: v1.ObjectMeta{
274+
Name:"test-pod-2",
275+
Namespace:"test-namespace2",
276+
CreationTimestamp: v1.Time{
277+
Time:time.Now().Add(time.Hour),
278+
},
279+
},
280+
Spec: corev1.PodSpec{
281+
Containers: []corev1.Container{
282+
{
283+
Env: []corev1.EnvVar{
284+
{
285+
Name:"CODER_AGENT_TOKEN",
286+
Value:"test-token-2",
287+
},
288+
},
289+
},
290+
},
291+
},
292+
}
293+
_,err=client.CoreV1().Pods("test-namespace2").Create(ctx,pod2, v1.CreateOptions{})
294+
require.NoError(t,err)
295+
296+
// Wait for both pods to be registered
297+
source1:=testutil.RequireRecvCtx(ctx,t,api.logSource)
298+
require.Equal(t,sourceUUID,source1.ID)
299+
require.Equal(t,"Kubernetes",source1.DisplayName)
300+
require.Equal(t,"/icon/k8s.png",source1.Icon)
301+
302+
source2:=testutil.RequireRecvCtx(ctx,t,api.logSource)
303+
require.Equal(t,sourceUUID,source2.ID)
304+
require.Equal(t,"Kubernetes",source2.DisplayName)
305+
require.Equal(t,"/icon/k8s.png",source2.Icon)
306+
307+
// Wait for both creation logs
308+
logs1:=testutil.RequireRecvCtx(ctx,t,api.logs)
309+
require.Len(t,logs1,1)
310+
require.Contains(t,logs1[0].Output,"Created pod")
311+
312+
logs2:=testutil.RequireRecvCtx(ctx,t,api.logs)
313+
require.Len(t,logs2,1)
314+
require.Contains(t,logs2[0].Output,"Created pod")
315+
316+
// Create an event in the first namespace
317+
event1:=&corev1.Event{
318+
ObjectMeta: v1.ObjectMeta{
319+
Name:"test-event-1",
320+
Namespace:"test-namespace1",
321+
CreationTimestamp: v1.Time{
322+
Time:time.Now().Add(time.Hour),
323+
},
324+
},
325+
InvolvedObject: corev1.ObjectReference{
326+
Kind:"Pod",
327+
Name:"test-pod-1",
328+
Namespace:"test-namespace1",
329+
},
330+
Reason:"Test",
331+
Message:"Test event for namespace1",
332+
}
333+
_,err=client.CoreV1().Events("test-namespace1").Create(ctx,event1, v1.CreateOptions{})
334+
require.NoError(t,err)
335+
336+
// Wait for the event log
337+
eventLogs:=testutil.RequireRecvCtx(ctx,t,api.logs)
338+
require.Len(t,eventLogs,1)
339+
require.Contains(t,eventLogs[0].Output,"Test event for namespace1")
340+
341+
// Create an event in the first namespace
342+
event2:=&corev1.Event{
343+
ObjectMeta: v1.ObjectMeta{
344+
Name:"test-event-2",
345+
Namespace:"test-namespace2",
346+
CreationTimestamp: v1.Time{
347+
Time:time.Now().Add(time.Hour),
348+
},
349+
},
350+
InvolvedObject: corev1.ObjectReference{
351+
Kind:"Pod",
352+
Name:"test-pod-2",
353+
Namespace:"test-namespace2",
354+
},
355+
Reason:"Test",
356+
Message:"Test event for namespace2",
357+
}
358+
_,err=client.CoreV1().Events("test-namespace2").Create(ctx,event2, v1.CreateOptions{})
359+
require.NoError(t,err)
360+
361+
// Wait for the event log
362+
eventLogs2:=testutil.RequireRecvCtx(ctx,t,api.logs)
363+
require.Len(t,eventLogs2,1)
364+
require.Contains(t,eventLogs2[0].Output,"Test event for namespace2")
365+
366+
// Clean up
367+
err=reporter.Close()
368+
require.NoError(t,err)
369+
}
370+
224371
funcTest_tokenCache(t*testing.T) {
225372
t.Parallel()
226373

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp