mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 20:36:18 +02:00
chore: use extracted talos-systems/go-kmsg library
This change uses extracted go-kmsg library (see https://github.com/talos-systems/go-kmsg/pull/1). Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
parent
79d804c5b4
commit
f2caed0df5
3
go.mod
3
go.mod
@ -70,6 +70,7 @@ require (
|
||||
github.com/talos-systems/crypto v0.2.1-0.20210427105118-4f80b976b640
|
||||
github.com/talos-systems/go-blockdevice v0.2.1-0.20210407132431-1d830a25f64f
|
||||
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0
|
||||
github.com/talos-systems/go-kmsg v0.1.0
|
||||
github.com/talos-systems/go-loadbalancer v0.1.0
|
||||
github.com/talos-systems/go-procfs v0.0.0-20210108152626-8cbc42d3dc24
|
||||
github.com/talos-systems/go-retry v0.2.1-0.20210119124456-b9dc1a990133
|
||||
@ -87,7 +88,7 @@ require (
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210427135350-f9ad6d392236
|
||||
|
||||
5
go.sum
5
go.sum
@ -1029,6 +1029,8 @@ github.com/talos-systems/go-blockdevice v0.2.1-0.20210407132431-1d830a25f64f h1:
|
||||
github.com/talos-systems/go-blockdevice v0.2.1-0.20210407132431-1d830a25f64f/go.mod h1:qnn/zDc09I1DA2BUDDCOSA2D0P8pIDjN8pGiRoRaQig=
|
||||
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0 h1:DI+BjK+fcrLBc70Fi50dZocQcaHosqsuWHrGHKp2NzE=
|
||||
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0/go.mod h1:kf+rZzTEmlDiYQ6ulslvRONnKLQH8x83TowltGMhO+k=
|
||||
github.com/talos-systems/go-kmsg v0.1.0 h1:juoZn+XioduYvtie6nqi/miKGJPLYSBNXRv5jRe6+lE=
|
||||
github.com/talos-systems/go-kmsg v0.1.0/go.mod h1:dppwQn+/mrdvsziGMbXjzfc4E+75oZhr39UIP6LgL0w=
|
||||
github.com/talos-systems/go-loadbalancer v0.1.0 h1:MQFONvSjoleU8RrKq1O1Z8CyTCJGd4SLqdAHDlR6o9s=
|
||||
github.com/talos-systems/go-loadbalancer v0.1.0/go.mod h1:D5Qjfz+29WVjONWECZvOkmaLsBb3f5YeWME0u/5HmIc=
|
||||
github.com/talos-systems/go-procfs v0.0.0-20210108152626-8cbc42d3dc24 h1:fN8vYvlB9XBQ5aImb1vLgR0ZaDwvfZfBMptqkpi3aEg=
|
||||
@ -1377,8 +1379,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
||||
@ -14,10 +14,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/talos-systems/go-kmsg"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
"github.com/talos-systems/talos/internal/pkg/mount"
|
||||
"github.com/talos-systems/talos/internal/pkg/mount/switchroot"
|
||||
"github.com/talos-systems/talos/pkg/machinery/constants"
|
||||
|
||||
@ -18,13 +18,13 @@ import (
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/talos-systems/go-kmsg"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
|
||||
containerdrunner "github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/containerd"
|
||||
"github.com/talos-systems/talos/internal/pkg/containers/image"
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
"github.com/talos-systems/talos/pkg/machinery/config"
|
||||
"github.com/talos-systems/talos/pkg/machinery/constants"
|
||||
|
||||
@ -33,6 +33,7 @@ import (
|
||||
"github.com/prometheus/procfs"
|
||||
"github.com/rs/xid"
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
|
||||
"github.com/talos-systems/go-kmsg"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/client/v3/concurrency"
|
||||
"golang.org/x/sys/unix"
|
||||
@ -54,7 +55,6 @@ import (
|
||||
"github.com/talos-systems/talos/internal/pkg/containers/cri"
|
||||
"github.com/talos-systems/talos/internal/pkg/containers/image"
|
||||
"github.com/talos-systems/talos/internal/pkg/etcd"
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
"github.com/talos-systems/talos/internal/pkg/kubeconfig"
|
||||
"github.com/talos-systems/talos/internal/pkg/mount"
|
||||
"github.com/talos-systems/talos/pkg/archiver"
|
||||
|
||||
@ -30,6 +30,7 @@ import (
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/util"
|
||||
"github.com/talos-systems/go-cmd/pkg/cmd"
|
||||
"github.com/talos-systems/go-kmsg"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/talos-systems/go-retry/retry"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
@ -52,7 +53,6 @@ import (
|
||||
"github.com/talos-systems/talos/internal/pkg/cri"
|
||||
"github.com/talos-systems/talos/internal/pkg/etcd"
|
||||
"github.com/talos-systems/talos/internal/pkg/kernel/kspp"
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
"github.com/talos-systems/talos/internal/pkg/mount"
|
||||
"github.com/talos-systems/talos/internal/pkg/partition"
|
||||
"github.com/talos-systems/talos/pkg/conditions"
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package kmsg provides access to kernel log.
|
||||
//
|
||||
package kmsg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// SetupLogger configures the logger to write to the kernel ring buffer via
|
||||
// /dev/kmsg.
|
||||
//
|
||||
// If logger is nil, default `log` logger is redirectred.
|
||||
//
|
||||
// If extraWriter is not nil, logs will be copied to it as well.
|
||||
func SetupLogger(logger *log.Logger, prefix string, extraWriter io.Writer) error {
|
||||
kmsg, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0o666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open /dev/kmsg: %w", err)
|
||||
}
|
||||
|
||||
var writer io.Writer = &Writer{KmsgWriter: kmsg}
|
||||
|
||||
if extraWriter != nil {
|
||||
writer = io.MultiWriter(writer, extraWriter)
|
||||
}
|
||||
|
||||
if logger != nil {
|
||||
logger.SetOutput(writer)
|
||||
logger.SetPrefix(prefix + " ")
|
||||
logger.SetFlags(0)
|
||||
} else {
|
||||
log.SetOutput(writer)
|
||||
log.SetPrefix(prefix + " ")
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Facility is an attribute of kernel log message.
|
||||
type Facility int
|
||||
|
||||
// Kernel log facilities.
|
||||
//
|
||||
// From <sys/syslog.h>.
|
||||
const (
|
||||
Kern Facility = iota
|
||||
User
|
||||
Mail
|
||||
Daemon
|
||||
Auth
|
||||
Syslog
|
||||
Lpr
|
||||
News
|
||||
Uucp
|
||||
Cron
|
||||
AuthPriv
|
||||
Local0
|
||||
Local1
|
||||
Local2
|
||||
Local3
|
||||
Local4
|
||||
Local5
|
||||
Local6
|
||||
Local7
|
||||
)
|
||||
|
||||
func (f Facility) String() string {
|
||||
return [...]string{
|
||||
"kern", "user", "mail", "daemon",
|
||||
"auth", "syslog", "lpr", "news", "uucp",
|
||||
"cron", "authpriv",
|
||||
"local0", "local1", "local2", "local3",
|
||||
"local4", "local5", "local6", "local7",
|
||||
}[f]
|
||||
}
|
||||
|
||||
// Priority is an attribute of kernel log message.
|
||||
type Priority int
|
||||
|
||||
// Kernel log priorities.
|
||||
const (
|
||||
Emerg Priority = iota
|
||||
Alert
|
||||
Crit
|
||||
Err
|
||||
Warning
|
||||
Notice
|
||||
Info
|
||||
Debug
|
||||
)
|
||||
|
||||
func (p Priority) String() string {
|
||||
return [...]string{"emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"}[p]
|
||||
}
|
||||
|
||||
// Message is a parsed kernel log message.
|
||||
type Message struct {
|
||||
Facility Facility
|
||||
Priority Priority
|
||||
SequenceNumber int64
|
||||
Clock int64
|
||||
Timestamp time.Time
|
||||
Message string
|
||||
}
|
||||
|
||||
// ParseMessage parses internal kernel log format.
|
||||
//
|
||||
// Reference: https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg
|
||||
func ParseMessage(input string, bootTime time.Time) (Message, error) {
|
||||
parts := strings.SplitN(input, ";", 2)
|
||||
if len(parts) != 2 {
|
||||
return Message{}, fmt.Errorf("kernel message should contain a prefix")
|
||||
}
|
||||
|
||||
prefix, message := parts[0], parts[1]
|
||||
|
||||
metadata := strings.Split(prefix, ",")
|
||||
if len(metadata) < 3 {
|
||||
return Message{}, fmt.Errorf("message metdata should have at least 3 parts, got %d", len(metadata))
|
||||
}
|
||||
|
||||
syslogPrefix, err := strconv.ParseInt(metadata[0], 10, 64)
|
||||
if err != nil {
|
||||
return Message{}, fmt.Errorf("error parsing priority: %w", err)
|
||||
}
|
||||
|
||||
sequence, err := strconv.ParseInt(metadata[1], 10, 64)
|
||||
if err != nil {
|
||||
return Message{}, fmt.Errorf("error parsing sequence: %w", err)
|
||||
}
|
||||
|
||||
clock, err := strconv.ParseInt(metadata[2], 10, 64)
|
||||
if err != nil {
|
||||
return Message{}, fmt.Errorf("errors parsing clock from boot: %w", err)
|
||||
}
|
||||
|
||||
return Message{
|
||||
Priority: Priority(syslogPrefix & 7),
|
||||
Facility: Facility(syslogPrefix >> 3),
|
||||
SequenceNumber: sequence,
|
||||
Clock: clock,
|
||||
Timestamp: bootTime.Add(time.Duration(clock) * time.Microsecond),
|
||||
Message: message,
|
||||
}, nil
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
)
|
||||
|
||||
func mustParse(tStr string) time.Time {
|
||||
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", tStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func TestParseMessage(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
input string
|
||||
expected kmsg.Message
|
||||
}{
|
||||
{
|
||||
input: `7,160,424069,-;pci_root PNP0A03:00: host bridge window [io 0x0000-0x0cf7] (ignored)
|
||||
SUBSYSTEM=acpi
|
||||
DEVICE=+acpi:PNP0A03:00`,
|
||||
expected: kmsg.Message{
|
||||
Facility: kmsg.Kern,
|
||||
Priority: kmsg.Debug,
|
||||
SequenceNumber: 160,
|
||||
Clock: 424069,
|
||||
Timestamp: mustParse("0001-01-01 00:00:00.424069 +0000 UTC"),
|
||||
Message: "pci_root PNP0A03:00: host bridge window [io 0x0000-0x0cf7] (ignored)\n SUBSYSTEM=acpi\n DEVICE=+acpi:PNP0A03:00",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `6,339,5140900,-;NET: Registered protocol family 10`,
|
||||
expected: kmsg.Message{
|
||||
Facility: kmsg.Kern,
|
||||
Priority: kmsg.Info,
|
||||
SequenceNumber: 339,
|
||||
Clock: 5140900,
|
||||
Timestamp: mustParse("0001-01-01 00:00:05.1409 +0000 UTC"),
|
||||
Message: "NET: Registered protocol family 10",
|
||||
},
|
||||
},
|
||||
} {
|
||||
message, err := kmsg.ParseMessage(testCase.input, time.Time{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testCase.expected, message)
|
||||
}
|
||||
}
|
||||
@ -1,200 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Packet combines Message and error.
|
||||
//
|
||||
// Only one of the fields is set in Reader.Scan.
|
||||
type Packet struct {
|
||||
Message Message
|
||||
Err error
|
||||
}
|
||||
|
||||
// Reader for /dev/kmsg messages.
|
||||
type Reader interface {
|
||||
// Scan and issue parsed messages.
|
||||
//
|
||||
// Scan stops when context is canceled or when EOF is reached
|
||||
// in NoFollow mode.
|
||||
Scan(ctx context.Context) <-chan Packet
|
||||
|
||||
// Close releases resources associated with the Reader.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Option configures Reader.
|
||||
type Option func(*options)
|
||||
|
||||
type options struct {
|
||||
follow bool
|
||||
tail bool
|
||||
}
|
||||
|
||||
// Follow the kmsg to stream live messages.
|
||||
func Follow() Option {
|
||||
return func(o *options) {
|
||||
o.follow = true
|
||||
}
|
||||
}
|
||||
|
||||
// FromTail starts reading kmsg from the tail (after last message).
|
||||
func FromTail() Option {
|
||||
return func(o *options) {
|
||||
o.tail = true
|
||||
}
|
||||
}
|
||||
|
||||
// NewReader initializes new /dev/kmsg reader.
|
||||
func NewReader(options ...Option) (Reader, error) {
|
||||
r := &reader{}
|
||||
|
||||
for _, o := range options {
|
||||
o(&r.options)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
r.bootTime, err = getBootTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.f, err = os.OpenFile("/dev/kmsg", os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.options.tail {
|
||||
_, err = r.f.Seek(0, os.SEEK_END)
|
||||
if err != nil {
|
||||
r.f.Close() //nolint:errcheck
|
||||
|
||||
return nil, fmt.Errorf("error seeking to the tail of kmsg: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
f *os.File
|
||||
options options
|
||||
bootTime time.Time
|
||||
}
|
||||
|
||||
func (r *reader) Close() error {
|
||||
return r.f.Close()
|
||||
}
|
||||
|
||||
func (r *reader) Scan(ctx context.Context) <-chan Packet {
|
||||
ch := make(chan Packet)
|
||||
|
||||
if r.options.follow {
|
||||
go r.scanFollow(ctx, ch)
|
||||
} else {
|
||||
go r.scanNoFollow(ctx, ch)
|
||||
}
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (r *reader) scanNoFollow(ctx context.Context, ch chan<- Packet) {
|
||||
defer close(ch)
|
||||
|
||||
fd := int(r.f.Fd())
|
||||
|
||||
if err := syscall.SetNonblock(fd, true); err != nil {
|
||||
select {
|
||||
case ch <- Packet{
|
||||
Err: fmt.Errorf("error switching to nonblock mode: %w", err),
|
||||
}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 8192)
|
||||
|
||||
for {
|
||||
n, err := syscall.Read(fd, buf)
|
||||
if err != nil {
|
||||
if err == io.EOF || err == syscall.EAGAIN {
|
||||
// end of file, done
|
||||
return
|
||||
}
|
||||
|
||||
if err == syscall.EPIPE {
|
||||
// buffer overrun, retry
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- Packet{
|
||||
Err: fmt.Errorf("error reading from kmsg: %w", err),
|
||||
}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var packet Packet
|
||||
packet.Message, packet.Err = ParseMessage(string(buf[:n]), r.bootTime)
|
||||
|
||||
select {
|
||||
case ch <- packet:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) scanFollow(ctx context.Context, ch chan<- Packet) {
|
||||
defer close(ch)
|
||||
|
||||
buf := make([]byte, 8192)
|
||||
|
||||
for {
|
||||
n, err := r.f.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// end of file, done
|
||||
return
|
||||
}
|
||||
|
||||
if err == syscall.EPIPE {
|
||||
// buffer overrun, retry
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- Packet{
|
||||
Err: fmt.Errorf("error reading from kmsg: %w", err),
|
||||
}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var packet Packet
|
||||
packet.Message, packet.Err = ParseMessage(string(buf[:n]), r.bootTime)
|
||||
|
||||
select {
|
||||
case ch <- packet:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
)
|
||||
|
||||
//nolint:thelper
|
||||
func skipIfNoKmsg(t *testing.T) {
|
||||
f, err := os.OpenFile("/dev/kmsg", os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Skip("/dev/kmsg is not available", err.Error())
|
||||
}
|
||||
|
||||
f.Close() //nolint:errcheck
|
||||
}
|
||||
|
||||
func TestReaderNoFollow(t *testing.T) {
|
||||
skipIfNoKmsg(t)
|
||||
|
||||
r, err := kmsg.NewReader()
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer r.Close() //nolint:errcheck
|
||||
|
||||
messageCount := 0
|
||||
|
||||
for packet := range r.Scan(context.Background()) {
|
||||
assert.NoError(t, packet.Err)
|
||||
|
||||
messageCount++
|
||||
}
|
||||
|
||||
assert.Greater(t, messageCount, 0)
|
||||
|
||||
assert.NoError(t, r.Close())
|
||||
}
|
||||
|
||||
func TestReaderFollow(t *testing.T) {
|
||||
testReaderFollow(t, true)
|
||||
}
|
||||
|
||||
func TestReaderFollowTail(t *testing.T) {
|
||||
testReaderFollow(t, false, kmsg.FromTail())
|
||||
}
|
||||
|
||||
//nolint:thelper
|
||||
func testReaderFollow(t *testing.T, expectMessages bool, options ...kmsg.Option) {
|
||||
skipIfNoKmsg(t)
|
||||
|
||||
r, err := kmsg.NewReader(append([]kmsg.Option{kmsg.Follow()}, options...)...)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer r.Close() //nolint:errcheck
|
||||
|
||||
messageCount := 0
|
||||
|
||||
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||
defer ctxCancel()
|
||||
|
||||
ch := r.Scan(ctx)
|
||||
|
||||
var closed bool
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case packet, ok := <-ch:
|
||||
if !ok {
|
||||
if !closed {
|
||||
assert.Fail(t, "channel closed before cancel")
|
||||
}
|
||||
|
||||
break LOOP
|
||||
}
|
||||
|
||||
if closed && errors.Is(packet.Err, os.ErrClosed) {
|
||||
// ignore 'file already closed' error as it might happen
|
||||
// from the branch below depending on whether context cancel or
|
||||
// read() finishes first
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(t, packet.Err)
|
||||
|
||||
messageCount++
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// abort
|
||||
closed = true
|
||||
ctxCancel()
|
||||
assert.NoError(t, r.Close())
|
||||
}
|
||||
}
|
||||
|
||||
if expectMessages {
|
||||
assert.Greater(t, messageCount, 0)
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getBootTime() (time.Time, error) {
|
||||
var sysinfo syscall.Sysinfo_t
|
||||
|
||||
err := syscall.Sysinfo(&sysinfo)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("could not get boot time: %w", err)
|
||||
}
|
||||
|
||||
// sysinfo only has seconds
|
||||
return time.Now().Add(-1 * (time.Duration(sysinfo.Uptime) * time.Second)), nil
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// MaxLineLength to be passed to kmsg, see https://github.com/torvalds/linux/blob/master/kernel/printk/printk.c#L450.
|
||||
const MaxLineLength = 1024 - 48
|
||||
|
||||
// Writer ensures writes by line and limits each line to maxLineLength characters.
|
||||
//
|
||||
// This workarounds kmsg limits.
|
||||
type Writer struct {
|
||||
KmsgWriter io.Writer
|
||||
}
|
||||
|
||||
// Write implements io.Writer interface.
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
// split writes by `\n`, and limit each line to MaxLineLength
|
||||
for len(p) > 0 {
|
||||
i := bytes.IndexByte(p, '\n')
|
||||
if i == -1 {
|
||||
i = len(p) - 1
|
||||
}
|
||||
|
||||
line := p[:i+1]
|
||||
if len(line) > MaxLineLength {
|
||||
line = append(line[:MaxLineLength-4], []byte("...\n")...)
|
||||
}
|
||||
|
||||
var nn int
|
||||
nn, err = w.KmsgWriter.Write(line)
|
||||
|
||||
if nn == len(line) {
|
||||
n += i + 1
|
||||
} else {
|
||||
n += nn
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
p = p[i+1:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package kmsg_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/talos-systems/talos/internal/pkg/kmsg"
|
||||
)
|
||||
|
||||
type fakeWriter struct {
|
||||
lines [][]byte
|
||||
}
|
||||
|
||||
func (w *fakeWriter) Write(p []byte) (n int, err error) {
|
||||
w.lines = append(w.lines, append([]byte(nil), p...))
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
fakeW := &fakeWriter{}
|
||||
kmsgW := &kmsg.Writer{KmsgWriter: fakeW}
|
||||
|
||||
n, err := kmsgW.Write([]byte("foo"))
|
||||
assert.Equal(t, 3, n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n, err = kmsgW.Write([]byte("bar\n"))
|
||||
assert.Equal(t, 4, n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n, err = kmsgW.Write([]byte("foo\nbar\n"))
|
||||
assert.Equal(t, 8, n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n, err = kmsgW.Write(append(bytes.Repeat([]byte{0xce}, kmsg.MaxLineLength-1), '\n'))
|
||||
assert.Equal(t, kmsg.MaxLineLength, n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n, err = kmsgW.Write(append(bytes.Repeat([]byte{0xce}, kmsg.MaxLineLength), '\n', 'a', 'b', '\n'))
|
||||
assert.Equal(t, kmsg.MaxLineLength+4, n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, fakeW.lines, 7)
|
||||
assert.Equal(t, fakeW.lines[0], []byte("foo"))
|
||||
assert.Equal(t, fakeW.lines[1], []byte("bar\n"))
|
||||
assert.Equal(t, fakeW.lines[2], []byte("foo\n"))
|
||||
assert.Equal(t, fakeW.lines[3], []byte("bar\n"))
|
||||
assert.Equal(t, fakeW.lines[4], append(bytes.Repeat([]byte{0xce}, kmsg.MaxLineLength-1), '\n'))
|
||||
assert.Equal(t, fakeW.lines[5], append(bytes.Repeat([]byte{0xce}, kmsg.MaxLineLength-4), '.', '.', '.', '\n'))
|
||||
assert.Equal(t, fakeW.lines[6], []byte("ab\n"))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user