Skip to content

Commit 949323a

Browse files
smirashanduur
authored andcommitted
feat: present kernel log as talosctl logs kernel
Extracted from #12115 The idea is that kernel log can be delivered/persisted along with any other service logs. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com> (cherry picked from commit e715f38)
1 parent 7531fcb commit 949323a

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

hack/release.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ The Kubernetes API server in Talos has been updated to use a more secure set of
203203
This is in line with a set of best practices documented in CIS 1.12 benchmark.
204204
205205
You can still expand the list of supported cipher suites via the `cluster.apiServer.extraArgs."tls-cipher-suites"` machine configuration field if needed.
206+
"""
207+
208+
[notes.kernel-log]
209+
title = "Kernel Log"
210+
description = """\
211+
The kernel log (dmesg) is now also available as the service log named `kernel` (`talosctl logs kernel`).
206212
"""
207213

208214
[make_deps]
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package runtime
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"time"
11+
12+
"github.com/cosi-project/runtime/pkg/controller"
13+
"github.com/siderolabs/go-kmsg"
14+
"go.uber.org/zap"
15+
16+
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
17+
)
18+
19+
// KmsgLogStorageController presents kernel message log as a 'kernel' log.
20+
type KmsgLogStorageController struct {
21+
V1Alpha1Logging runtime.LoggingManager
22+
V1Alpha1Mode runtime.Mode
23+
}
24+
25+
// Name implements controller.Controller interface.
26+
func (ctrl *KmsgLogStorageController) Name() string {
27+
return "runtime.KmsgLogStorageController"
28+
}
29+
30+
// Inputs implements controller.Controller interface.
31+
func (ctrl *KmsgLogStorageController) Inputs() []controller.Input {
32+
return nil
33+
}
34+
35+
// Outputs implements controller.Controller interface.
36+
func (ctrl *KmsgLogStorageController) Outputs() []controller.Output {
37+
return nil
38+
}
39+
40+
// Run implements controller.Controller interface.
41+
//
42+
//nolint:gocyclo
43+
func (ctrl *KmsgLogStorageController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
44+
if ctrl.V1Alpha1Mode.InContainer() {
45+
return nil
46+
}
47+
48+
var err error
49+
50+
logWriter, err := ctrl.V1Alpha1Logging.ServiceLog("kernel").Writer()
51+
if err != nil {
52+
return fmt.Errorf("error opening logger: %w", err)
53+
}
54+
defer logWriter.Close() //nolint:errcheck
55+
56+
// initilalize kmsg reader early, so that we don't lose position on config changes
57+
reader, err := kmsg.NewReader(kmsg.Follow())
58+
if err != nil {
59+
return fmt.Errorf("error reading kernel messages: %w", err)
60+
}
61+
62+
defer reader.Close() //nolint:errcheck
63+
64+
kmsgCh := reader.Scan(ctx)
65+
66+
// wait for the initial event to start processing messages
67+
select {
68+
case <-ctx.Done():
69+
return nil
70+
case <-r.EventCh():
71+
}
72+
73+
for {
74+
var msg kmsg.Packet
75+
76+
select {
77+
case <-ctx.Done():
78+
return nil
79+
case msg = <-kmsgCh:
80+
}
81+
82+
if msg.Err != nil {
83+
return fmt.Errorf("error receiving kernel logs: %w", msg.Err)
84+
}
85+
86+
if _, err = logWriter.Write(
87+
fmt.Appendf(nil, "%s: %7s: [%s]: %s", msg.Message.Facility, msg.Message.Priority, msg.Message.Timestamp.Format(time.RFC3339Nano), msg.Message.Message),
88+
); err != nil {
89+
return err
90+
}
91+
}
92+
}

internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,10 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
423423
&runtimecontrollers.KmsgLogDeliveryController{
424424
Drainer: drainer,
425425
},
426+
&runtimecontrollers.KmsgLogStorageController{
427+
V1Alpha1Logging: ctrl.v1alpha1Runtime.Logging(),
428+
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
429+
},
426430
&runtimecontrollers.LoadedKernelModuleController{
427431
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
428432
},

internal/integration/api/logs.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import (
1010
"bufio"
1111
"bytes"
1212
"context"
13+
"fmt"
1314
"io"
15+
"strings"
1416
"time"
1517

18+
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging"
1619
"github.com/siderolabs/talos/internal/integration/base"
1720
"github.com/siderolabs/talos/pkg/machinery/api/common"
1821
"github.com/siderolabs/talos/pkg/machinery/client"
@@ -108,6 +111,46 @@ func (suite *LogsSuite) TestAuditdLogs() {
108111
suite.Require().Greater(n, int64(1024))
109112
}
110113

114+
// TestKernelLogs verifies that kernel logs are present and valid.
115+
func (suite *LogsSuite) TestKernelLogs() {
116+
if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU {
117+
suite.T().Skip("skip kernel logs test for non-QEMU clusters")
118+
}
119+
120+
logsStream, err := suite.Client.Logs(
121+
suite.nodeCtx,
122+
constants.SystemContainerdNamespace,
123+
common.ContainerDriver_CONTAINERD,
124+
"kernel",
125+
false,
126+
-1,
127+
)
128+
suite.Require().NoError(err)
129+
130+
logReader, err := client.ReadStream(logsStream)
131+
suite.Require().NoError(err)
132+
133+
var buf bytes.Buffer
134+
135+
n, err := io.Copy(&buf, logReader)
136+
suite.Require().NoError(err)
137+
138+
// kernel logs shouldn't be empty
139+
suite.Require().Greater(n, int64(1024))
140+
141+
version := strings.Contains(buf.String(), fmt.Sprintf("Linux version %s", constants.DefaultKernelVersion))
142+
overflow := n >= logging.DesiredCapacity/2
143+
144+
if !version && overflow {
145+
suite.T().Skip("skipping the test as kernel log buffer might have overflowed")
146+
}
147+
148+
suite.Require().Truef(
149+
version,
150+
"did not find kernel version (%s) in the log buffer", constants.DefaultKernelVersion,
151+
)
152+
}
153+
111154
// TestTail verifies that log tail might be requested.
112155
func (suite *LogsSuite) TestTail() {
113156
// invoke machined enough times to generate

0 commit comments

Comments
 (0)