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

feat: add multi-namespace support#124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
kacpersaw merged 3 commits intomainfromkacpersaw/feat-watch-multiple-namespaces
Aug 26, 2025
Merged
Show file tree
Hide file tree
Changes fromall 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: 14 additions & 0 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,20 @@ helm install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
--set url=<your-coder-url-including-http-or-https>
```

> **Multi-Namespace support**
>
> 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:
>
> ```yaml
> # Watch specific namespaces only
> namespaces: ["default", "kube-system"]
>
> # Watch all namespaces (default)
> namespaces: []
> ```
>
> When `namespaces` is empty or not specified, the service will monitor all namespaces in the cluster.

> **Note**
> For additional customization (such as customizing the image, pull secrets, annotations, etc.), you can use the
> [values.yaml](helm/values.yaml) file directly.
Expand Down
59 changes: 47 additions & 12 deletionshelm/templates/service.yaml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: coder-logstream-kube-role
rules:
{{- define "coder-logstream-kube.rules" -}}
- apiGroups: [""]
resources: ["pods", "events"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
resources: ["replicasets", "events"]
verbs: ["get", "watch", "list"]
{{- end -}}

{{- if .Values.namespaces }}
{{- range .Values.namespaces }}
---
apiVersion: v1
kind:ServiceAccount
apiVersion:rbac.authorization.k8s.io/v1
kind:Role
metadata:
name: {{ .Values.serviceAccount.name | quote }}
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
name: coder-logstream-kube-role
namespace: {{ . }}
rules:
{{ include "coder-logstream-kube.rules" . | nindent 2 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: coder-logstream-kube-rolebinding
namespace: {{ . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: coder-logstream-kube-role
subjects:
- kind: ServiceAccount
name: {{ $.Values.serviceAccount.name | quote }}
namespace: {{ $.Release.Namespace }}
{{- end }}
{{- else }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: coder-logstream-kube-role
rules:
{{ include "coder-logstream-kube.rules" . | nindent 2 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: coder-logstream-kube-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: coder-logstream-kube-role
subjects:
- kind: ServiceAccount
name: {{ .Values.serviceAccount.name | quote }}
namespace: {{ .Release.Namespace }}
{{- end }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccount.name | quote }}
annotations: {{ toYaml .Values.serviceAccount.annotations | nindent 4 }}
labels: {{ toYaml .Values.serviceAccount.labels | nindent 4 }}
---
apiVersion: apps/v1
kind: Deployment
Expand DownExpand Up@@ -75,8 +108,10 @@ spec:
env:
- name: CODER_URL
value: {{ .Values.url }}
- name: CODER_NAMESPACE
value: {{ .Values.namespace | default .Release.Namespace }}
{{- if .Values.namespaces }}
- name: CODER_NAMESPACES
value: {{ join "," .Values.namespaces }}
{{- end }}
{{- if .Values.image.sslCertFile }}
- name: SSL_CERT_FILE
value: {{ .Values.image.sslCertFile }}
Expand Down
6 changes: 3 additions & 3 deletionshelm/values.yaml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
# url -- The URL of your Coder deployment. Must prefix with http or https
url:""

# namespace --The namespacetosearching for Pods within.
# If unspecified, this defaults to the Helm namespace.
namespace:""
# namespace --List of namespacestosearch for Pods within.
# If unspecified or empty it will watch all namespaces.
namespaces:[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is breaking so make sure you inform customers in the release notes.


# volumes -- A list of extra volumes to add to the coder-logstream pod.
volumes:
Expand Down
26 changes: 19 additions & 7 deletionslogger.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,7 +36,7 @@ type podEventLoggerOptions struct {
logDebounce time.Duration

// The following fields are optional!
namespacestring
namespaces[]string
fieldSelector string
labelSelector string
}
Expand DownExpand Up@@ -78,7 +78,18 @@ func newPodEventLogger(ctx context.Context, opts podEventLoggerOptions) (*podEve
},
}

return reporter, reporter.init()
// If no namespaces are provided, we listen for events in all namespaces.
if len(opts.namespaces) == 0 {
reporter.initNamespace("")
} else {
for _, namespace := range opts.namespaces {
if err := reporter.initNamespace(namespace); err != nil {
return nil, err
}
}
}

return reporter, nil
}

type podEventLogger struct {
Expand All@@ -95,22 +106,23 @@ type podEventLogger struct {
lq *logQueuer
}

// init starts the informer factory and registers event handlers.
func (p *podEventLogger) init() error {
// initNamespace starts the informer factory and registers event handlers for a given namespace.
// If provided namespace is empty, it will start the informer factory and register event handlers for all namespaces.
func (p *podEventLogger) initNamespace(namespace string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Should include a sentence in the method comment explaining thenamespace parameter

kacpersaw reacted with thumbs up emoji
// We only track events that happen after the reporter starts.
// This is to prevent us from sending duplicate events.
startTime := time.Now()

go p.lq.work(p.ctx)

podFactory := informers.NewSharedInformerFactoryWithOptions(p.client, 0, informers.WithNamespace(p.namespace), informers.WithTweakListOptions(func(lo *v1.ListOptions) {
podFactory := informers.NewSharedInformerFactoryWithOptions(p.client, 0, informers.WithNamespace(namespace), informers.WithTweakListOptions(func(lo *v1.ListOptions) {
lo.FieldSelector = p.fieldSelector
lo.LabelSelector = p.labelSelector
}))
eventFactory := podFactory
if p.fieldSelector != "" || p.labelSelector != "" {
// Events cannot filter on labels and fields!
eventFactory = informers.NewSharedInformerFactoryWithOptions(p.client, 0, informers.WithNamespace(p.namespace))
eventFactory = informers.NewSharedInformerFactoryWithOptions(p.client, 0, informers.WithNamespace(namespace))
}

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

p.logger.Info(p.ctx, "listening for pod events",
slog.F("coder_url", p.coderURL.String()),
slog.F("namespace",p.namespace),
slog.F("namespace", namespace),
slog.F("field_selector", p.fieldSelector),
slog.F("label_selector", p.labelSelector),
)
Expand Down
151 changes: 149 additions & 2 deletionslogger_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@ func TestReplicaSetEvents(t *testing.T) {
reporter, err := newPodEventLogger(ctx, podEventLoggerOptions{
client: client,
coderURL: agentURL,
namespace:namespace,
namespaces:[]string{namespace},
logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
logDebounce: 5 * time.Second,
clock: cMock,
Expand DownExpand Up@@ -144,7 +144,7 @@ func TestPodEvents(t *testing.T) {
reporter, err := newPodEventLogger(ctx, podEventLoggerOptions{
client: client,
coderURL: agentURL,
namespace:namespace,
namespaces:[]string{namespace},
logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
logDebounce: 5 * time.Second,
clock: cMock,
Expand DownExpand Up@@ -221,6 +221,153 @@ func TestPodEvents(t *testing.T) {
require.NoError(t, err)
}

func Test_newPodEventLogger_multipleNamespaces(t *testing.T) {
t.Parallel()

api := newFakeAgentAPI(t)

ctx := testutil.Context(t, testutil.WaitShort)
agentURL, err := url.Parse(api.server.URL)
require.NoError(t, err)
namespaces := []string{"test-namespace1", "test-namespace2"}
client := fake.NewSimpleClientset()

cMock := quartz.NewMock(t)
reporter, err := newPodEventLogger(ctx, podEventLoggerOptions{
client: client,
coderURL: agentURL,
namespaces: namespaces,
logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
logDebounce: 5 * time.Second,
clock: cMock,
})
require.NoError(t, err)

// Create a pod in the test-namespace1 namespace
pod1 := &corev1.Pod{
ObjectMeta: v1.ObjectMeta{
Name: "test-pod-1",
Namespace: "test-namespace1",
CreationTimestamp: v1.Time{
Time: time.Now().Add(time.Hour),
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: "CODER_AGENT_TOKEN",
Value: "test-token-1",
},
},
},
},
},
}
_, err = client.CoreV1().Pods("test-namespace1").Create(ctx, pod1, v1.CreateOptions{})
require.NoError(t, err)

// Create a pod in the test-namespace2 namespace
pod2 := &corev1.Pod{
ObjectMeta: v1.ObjectMeta{
Name: "test-pod-2",
Namespace: "test-namespace2",
CreationTimestamp: v1.Time{
Time: time.Now().Add(time.Hour),
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: "CODER_AGENT_TOKEN",
Value: "test-token-2",
},
},
},
},
},
}
_, err = client.CoreV1().Pods("test-namespace2").Create(ctx, pod2, v1.CreateOptions{})
require.NoError(t, err)

// Wait for both pods to be registered
source1 := testutil.RequireRecvCtx(ctx, t, api.logSource)
require.Equal(t, sourceUUID, source1.ID)
require.Equal(t, "Kubernetes", source1.DisplayName)
require.Equal(t, "/icon/k8s.png", source1.Icon)

source2 := testutil.RequireRecvCtx(ctx, t, api.logSource)
require.Equal(t, sourceUUID, source2.ID)
require.Equal(t, "Kubernetes", source2.DisplayName)
require.Equal(t, "/icon/k8s.png", source2.Icon)

// Wait for both creation logs
logs1 := testutil.RequireRecvCtx(ctx, t, api.logs)
require.Len(t, logs1, 1)
require.Contains(t, logs1[0].Output, "Created pod")

logs2 := testutil.RequireRecvCtx(ctx, t, api.logs)
require.Len(t, logs2, 1)
require.Contains(t, logs2[0].Output, "Created pod")

// Create an event in the first namespace
event1 := &corev1.Event{
ObjectMeta: v1.ObjectMeta{
Name: "test-event-1",
Namespace: "test-namespace1",
CreationTimestamp: v1.Time{
Time: time.Now().Add(time.Hour),
},
},
InvolvedObject: corev1.ObjectReference{
Kind: "Pod",
Name: "test-pod-1",
Namespace: "test-namespace1",
},
Reason: "Test",
Message: "Test event for namespace1",
}
_, err = client.CoreV1().Events("test-namespace1").Create(ctx, event1, v1.CreateOptions{})
require.NoError(t, err)

// Wait for the event log
eventLogs := testutil.RequireRecvCtx(ctx, t, api.logs)
require.Len(t, eventLogs, 1)
require.Contains(t, eventLogs[0].Output, "Test event for namespace1")

// Create an event in the first namespace
event2 := &corev1.Event{
ObjectMeta: v1.ObjectMeta{
Name: "test-event-2",
Namespace: "test-namespace2",
CreationTimestamp: v1.Time{
Time: time.Now().Add(time.Hour),
},
},
InvolvedObject: corev1.ObjectReference{
Kind: "Pod",
Name: "test-pod-2",
Namespace: "test-namespace2",
},
Reason: "Test",
Message: "Test event for namespace2",
}
_, err = client.CoreV1().Events("test-namespace2").Create(ctx, event2, v1.CreateOptions{})
require.NoError(t, err)

// Wait for the event log
eventLogs2 := testutil.RequireRecvCtx(ctx, t, api.logs)
require.Len(t, eventLogs2, 1)
require.Contains(t, eventLogs2[0].Output, "Test event for namespace2")

// Clean up
err = reporter.Close()
require.NoError(t, err)
}

func Test_tokenCache(t *testing.T) {
t.Parallel()

Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp