feat: move dashboard package & run it in tty2
Move dashboard package into a common location where both Talos and talosctl can use it. Add support for overriding stdin, stdout, stderr and ctt in process runner. Create a dashboard service which runs the dashboard on /dev/tty2. Redirect kernel messages to tty1 and switch to tty2 after starting the dashboard on it. Related to siderolabs/talos#6841, siderolabs/talos#4791. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
This commit is contained in:
parent
36e077ead4
commit
f55f5df739
@ -476,6 +476,8 @@ RUN ln /rootfs/sbin/init /rootfs/sbin/poweroff
|
||||
RUN chmod +x /rootfs/sbin/poweroff
|
||||
RUN ln /rootfs/sbin/init /rootfs/sbin/wrapperd
|
||||
RUN chmod +x /rootfs/sbin/wrapperd
|
||||
RUN ln /rootfs/sbin/init /rootfs/sbin/dashboard
|
||||
RUN chmod +x /rootfs/sbin/dashboard
|
||||
# NB: We run the cleanup step before creating extra directories, files, and
|
||||
# symlinks to avoid accidentally cleaning them up.
|
||||
COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
|
||||
@ -525,6 +527,8 @@ RUN ln /rootfs/sbin/init /rootfs/sbin/poweroff
|
||||
RUN chmod +x /rootfs/sbin/poweroff
|
||||
RUN ln /rootfs/sbin/init /rootfs/sbin/wrapperd
|
||||
RUN chmod +x /rootfs/sbin/wrapperd
|
||||
RUN ln /rootfs/sbin/init /rootfs/sbin/dashboard
|
||||
RUN chmod +x /rootfs/sbin/dashboard
|
||||
# NB: We run the cleanup step before creating extra directories, files, and
|
||||
# symlinks to avoid accidentally cleaning them up.
|
||||
COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard"
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
)
|
||||
|
||||
@ -38,7 +38,7 @@ Keyboard shortcuts:
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return WithClient(func(ctx context.Context, c *client.Client) error {
|
||||
return dashboard.Main(ctx, c, dashboardCmdFlags.interval)
|
||||
return dashboard.Main(ctx, c, dashboardCmdFlags.interval, true)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
@ -77,6 +77,19 @@ machine:
|
||||
title = "Machine Configuration"
|
||||
description="""\
|
||||
Strategic merge config patches correctly support merging `.vlans` sections of the network interface.
|
||||
"""
|
||||
|
||||
[notes.dashboard]
|
||||
title = "Talos Dashboard on TTY2"
|
||||
description="""\
|
||||
Talos now starts a text-based UI dashboard on virtual console `/dev/tty2` and switches to it by default upon boot.
|
||||
Kernel logs remain available on `/dev/tty1`.
|
||||
|
||||
To switch TTYs, use the `Alt+F1` through `Alt+F2` keys.
|
||||
|
||||
You can disable this behavior by setting the kernel parameter `talos.dashboard.disabled=1`.
|
||||
|
||||
This behavior is disabled by default on SBCs.
|
||||
"""
|
||||
|
||||
[make_deps]
|
||||
|
49
internal/app/dashboard/main.go
Normal file
49
internal/app/dashboard/main.go
Normal file
@ -0,0 +1,49 @@
|
||||
// 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 dashboard implements dashboard functionality.
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard"
|
||||
"github.com/siderolabs/talos/pkg/grpc/middleware/authz"
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/role"
|
||||
"github.com/siderolabs/talos/pkg/startup"
|
||||
)
|
||||
|
||||
// Main is the entrypoint into dashboard.
|
||||
func Main() {
|
||||
if err := dashboardMain(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func dashboardMain() error {
|
||||
startup.LimitMaxProcs(constants.DashboardMaxProcs)
|
||||
|
||||
md := metadata.Pairs()
|
||||
authz.SetMetadata(md, role.MakeSet(role.Admin))
|
||||
adminCtx := metadata.NewOutgoingContext(context.Background(), md)
|
||||
|
||||
c, err := client.New(adminCtx,
|
||||
client.WithUnixSocket(constants.MachineSocketPath),
|
||||
client.WithGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error connecting to the machine service: %w", err)
|
||||
}
|
||||
|
||||
return dashboard.Main(adminCtx, c, 5*time.Second, false)
|
||||
}
|
@ -24,6 +24,7 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/apid"
|
||||
"github.com/siderolabs/talos/internal/app/dashboard"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
|
||||
@ -316,6 +317,10 @@ func main() {
|
||||
case "/sbin/wrapperd":
|
||||
wrapperd.Main()
|
||||
|
||||
return
|
||||
case "/sbin/dashboard":
|
||||
dashboard.Main()
|
||||
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
@ -94,6 +94,7 @@ func (b *BananaPiM64) Install(disk string) (err error) {
|
||||
func (b *BananaPiM64) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS0,115200"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ func (b JetsonNano) KernelArgs() procfs.Parameters {
|
||||
// trying to kexec. Seems the drivers state is not reset properly.
|
||||
// disabling kexec until we have further knowledge on this
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ func (l *LibretechAllH3CCH5) Install(disk string) (err error) {
|
||||
func (l *LibretechAllH3CCH5) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS0,115200"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ func (n *NanoPiR4S) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS2,1500000n8"),
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ func (b Pine64) Install(disk string) (err error) {
|
||||
func (b Pine64) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS0,115200"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ func (r *Rock64) Install(disk string) (err error) {
|
||||
func (r *Rock64) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS2,115200n8"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ func (r *Rockpi4) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS2,1500000n8"),
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ func (r *Rockpi4c) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyS2,1500000n8"),
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ func (r *RPi4) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyAMA0,115200"),
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ func (r *RPiGeneric) KernelArgs() procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty0").Append("ttyAMA0,115200"),
|
||||
procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"),
|
||||
procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,9 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/siderolabs/go-pointer"
|
||||
"github.com/siderolabs/go-procfs/procfs"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
@ -106,6 +109,15 @@ func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase {
|
||||
"earlyServices",
|
||||
StartUdevd,
|
||||
StartMachined,
|
||||
).AppendWithDeferredCheck(
|
||||
func() bool {
|
||||
disabledStr := procfs.ProcCmdline().Get(constants.KernelParamDashboardDisabled).First()
|
||||
disabled, _ := strconv.ParseBool(pointer.SafeDeref(disabledStr)) //nolint:errcheck
|
||||
|
||||
return !disabled
|
||||
},
|
||||
"dashboard",
|
||||
StartDashboard,
|
||||
).AppendWithDeferredCheck(
|
||||
func() bool {
|
||||
return r.State().Machine().Installed()
|
||||
|
@ -54,6 +54,7 @@ import (
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/events"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/services"
|
||||
"github.com/siderolabs/talos/internal/app/maintenance"
|
||||
"github.com/siderolabs/talos/internal/pkg/console"
|
||||
"github.com/siderolabs/talos/internal/pkg/cri"
|
||||
"github.com/siderolabs/talos/internal/pkg/etcd"
|
||||
"github.com/siderolabs/talos/internal/pkg/install"
|
||||
@ -203,6 +204,15 @@ func CreateSystemCgroups(seq runtime.Sequence, data interface{}) (runtime.TaskEx
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: constants.CgroupDashboard,
|
||||
resources: &cgroupsv2.Resources{
|
||||
Memory: &cgroupsv2.Memory{
|
||||
Min: pointer.To[int64](constants.CgroupDashboardReservedMemory),
|
||||
Low: pointer.To[int64](constants.CgroupDashboardLowMemory),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range groups {
|
||||
@ -792,6 +802,19 @@ func StartMachined(_ runtime.Sequence, _ interface{}) (runtime.TaskExecutionFunc
|
||||
}, "startMachined"
|
||||
}
|
||||
|
||||
// StartDashboard represents the task to start dashboard.
|
||||
func StartDashboard(_ runtime.Sequence, _ interface{}) (runtime.TaskExecutionFunc, string) {
|
||||
return func(_ context.Context, _ *log.Logger, r runtime.Runtime) error {
|
||||
ttyNumber := constants.DashboardTTY
|
||||
|
||||
system.Services(r).LoadAndStart(&services.Dashboard{
|
||||
TTYNumber: ttyNumber,
|
||||
})
|
||||
|
||||
return console.Switch(ttyNumber)
|
||||
}, "startDashboard"
|
||||
}
|
||||
|
||||
// StartUdevd represents the task to start udevd.
|
||||
func StartUdevd(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
|
||||
|
@ -76,16 +76,25 @@ func (p *processRunner) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *processRunner) build() (cmd *exec.Cmd, logCloser io.Closer, err error) {
|
||||
type commandWrapper struct {
|
||||
cmd *exec.Cmd
|
||||
afterStart func()
|
||||
afterTermination func() error
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (p *processRunner) build() (commandWrapper, error) {
|
||||
args := []string{
|
||||
fmt.Sprintf("-name=%s", p.args.ID),
|
||||
fmt.Sprintf("-dropped-caps=%s", strings.Join(p.opts.DroppedCapabilities, ",")),
|
||||
fmt.Sprintf("-cgroup-path=%s", p.opts.CgroupPath),
|
||||
fmt.Sprintf("-oom-score=%d", p.opts.OOMScoreAdj),
|
||||
fmt.Sprintf("-uid=%d", p.opts.UID),
|
||||
}
|
||||
|
||||
args = append(args, p.args.ProcessArgs...)
|
||||
|
||||
cmd = exec.Command("/sbin/wrapperd", args...)
|
||||
cmd := exec.Command("/sbin/wrapperd", args...)
|
||||
|
||||
// Set the environment for the service.
|
||||
cmd.Env = append([]string{fmt.Sprintf("PATH=%s", constants.PATH)}, p.opts.Env...)
|
||||
@ -93,9 +102,7 @@ func (p *processRunner) build() (cmd *exec.Cmd, logCloser io.Closer, err error)
|
||||
// Setup logging.
|
||||
w, err := p.opts.LoggingManager.ServiceLog(p.args.ID).Writer()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("service log handler: %w", err)
|
||||
|
||||
return
|
||||
return commandWrapper{}, fmt.Errorf("service log handler: %w", err)
|
||||
}
|
||||
|
||||
var writer io.Writer
|
||||
@ -105,20 +112,92 @@ func (p *processRunner) build() (cmd *exec.Cmd, logCloser io.Closer, err error)
|
||||
writer = w
|
||||
}
|
||||
|
||||
cmd.Stdout = writer
|
||||
cmd.Stderr = writer
|
||||
// close the writer if we exit early due to an error
|
||||
closeWriter := true
|
||||
|
||||
return cmd, w, nil
|
||||
defer func() {
|
||||
if closeWriter {
|
||||
w.Close() //nolint:errcheck
|
||||
}
|
||||
}()
|
||||
|
||||
var afterStartFuncs []func()
|
||||
|
||||
if p.opts.StdinFile != "" {
|
||||
stdin, err := os.Open(p.opts.StdinFile)
|
||||
if err != nil {
|
||||
return commandWrapper{}, err
|
||||
}
|
||||
|
||||
cmd.Stdin = stdin
|
||||
|
||||
afterStartFuncs = append(afterStartFuncs, func() {
|
||||
stdin.Close() //nolint:errcheck
|
||||
})
|
||||
}
|
||||
|
||||
if p.opts.StdoutFile != "" {
|
||||
stdout, err := os.OpenFile(p.opts.StdoutFile, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return commandWrapper{}, err
|
||||
}
|
||||
|
||||
cmd.Stdout = stdout
|
||||
|
||||
afterStartFuncs = append(afterStartFuncs, func() {
|
||||
stdout.Close() //nolint:errcheck
|
||||
})
|
||||
} else {
|
||||
cmd.Stdout = writer
|
||||
}
|
||||
|
||||
if p.opts.StderrFile != "" {
|
||||
stderr, err := os.OpenFile(p.opts.StderrFile, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return commandWrapper{}, err
|
||||
}
|
||||
|
||||
cmd.Stderr = stderr
|
||||
|
||||
afterStartFuncs = append(afterStartFuncs, func() {
|
||||
stderr.Close() //nolint:errcheck
|
||||
})
|
||||
} else {
|
||||
cmd.Stderr = writer
|
||||
}
|
||||
|
||||
ctty, cttySet := p.opts.Ctty.Get()
|
||||
if cttySet {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
Setctty: true,
|
||||
Ctty: ctty,
|
||||
}
|
||||
}
|
||||
|
||||
closeWriter = false
|
||||
|
||||
return commandWrapper{
|
||||
cmd: cmd,
|
||||
afterStart: func() {
|
||||
for _, f := range afterStartFuncs {
|
||||
f()
|
||||
}
|
||||
},
|
||||
afterTermination: func() error {
|
||||
return w.Close()
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (p *processRunner) run(eventSink events.Recorder) error {
|
||||
cmd, logCloser, err := p.build()
|
||||
cmdWrapper, err := p.build()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building command: %w", err)
|
||||
}
|
||||
|
||||
defer logCloser.Close() //nolint:errcheck
|
||||
defer cmdWrapper.afterTermination() //nolint:errcheck
|
||||
|
||||
notifyCh := make(chan reaper.ProcessInfo, 8)
|
||||
|
||||
@ -127,16 +206,20 @@ func (p *processRunner) run(eventSink events.Recorder) error {
|
||||
defer reaper.Stop(notifyCh)
|
||||
}
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
err = cmdWrapper.cmd.Start()
|
||||
|
||||
cmdWrapper.afterStart()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting process: %w", err)
|
||||
}
|
||||
|
||||
eventSink(events.StateRunning, "Process %s started with PID %d", p, cmd.Process.Pid)
|
||||
eventSink(events.StateRunning, "Process %s started with PID %d", p, cmdWrapper.cmd.Process.Pid)
|
||||
|
||||
waitCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
waitCh <- reaper.WaitWrapper(usingReaper, notifyCh, cmd)
|
||||
waitCh <- reaper.WaitWrapper(usingReaper, notifyCh, cmdWrapper.cmd)
|
||||
}()
|
||||
|
||||
select {
|
||||
@ -148,7 +231,7 @@ func (p *processRunner) run(eventSink events.Recorder) error {
|
||||
eventSink(events.StateStopping, "Sending SIGTERM to %s", p)
|
||||
|
||||
//nolint:errcheck
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM)
|
||||
_ = cmdWrapper.cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
|
||||
select {
|
||||
@ -160,13 +243,13 @@ func (p *processRunner) run(eventSink events.Recorder) error {
|
||||
eventSink(events.StateStopping, "Sending SIGKILL to %s", p)
|
||||
|
||||
//nolint:errcheck
|
||||
_ = cmd.Process.Signal(syscall.SIGKILL)
|
||||
_ = cmdWrapper.cmd.Process.Signal(syscall.SIGKILL)
|
||||
}
|
||||
|
||||
// wait for process to terminate
|
||||
<-waitCh
|
||||
|
||||
return logCloser.Close()
|
||||
return cmdWrapper.afterTermination()
|
||||
}
|
||||
|
||||
func (p *processRunner) String() string {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/siderolabs/gen/maps"
|
||||
"github.com/siderolabs/gen/optional"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging"
|
||||
@ -66,6 +67,16 @@ type Options struct {
|
||||
OverrideSeccompProfile func(*specs.LinuxSeccomp)
|
||||
// DroppedCapabilities is the list of capabilities to drop.
|
||||
DroppedCapabilities []string
|
||||
// StdinFile is the path to the file to use as stdin.
|
||||
StdinFile string
|
||||
// StdoutFile is the path to the file to use as stdout.
|
||||
StdoutFile string
|
||||
// StderrFile is the path to the file to use as stderr.
|
||||
StderrFile string
|
||||
// Ctty is the controlling tty.
|
||||
Ctty optional.Optional[int]
|
||||
// UID is the user id of the process.
|
||||
UID uint32
|
||||
}
|
||||
|
||||
// Option is the functional option func.
|
||||
@ -174,3 +185,38 @@ func WithDroppedCapabilities(caps map[string]struct{}) Option {
|
||||
args.DroppedCapabilities = maps.Keys(caps)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStdinFile sets the path to the file to use as stdin.
|
||||
func WithStdinFile(path string) Option {
|
||||
return func(args *Options) {
|
||||
args.StdinFile = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithStdoutFile sets the path to the file to use as stdout.
|
||||
func WithStdoutFile(path string) Option {
|
||||
return func(args *Options) {
|
||||
args.StdoutFile = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithStderrFile sets the path to the file to use as stderr.
|
||||
func WithStderrFile(path string) Option {
|
||||
return func(args *Options) {
|
||||
args.StdoutFile = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithCtty sets the controlling tty.
|
||||
func WithCtty(ctty int) Option {
|
||||
return func(args *Options) {
|
||||
args.Ctty = optional.Some(ctty)
|
||||
}
|
||||
}
|
||||
|
||||
// WithUID sets the user id of the process.
|
||||
func WithUID(uid uint32) Option {
|
||||
return func(args *Options) {
|
||||
args.UID = uid
|
||||
}
|
||||
}
|
||||
|
73
internal/app/machined/pkg/system/services/dashboard.go
Normal file
73
internal/app/machined/pkg/system/services/dashboard.go
Normal file
@ -0,0 +1,73 @@
|
||||
// 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/.
|
||||
|
||||
//nolint:golint,dupl
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/events"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
|
||||
"github.com/siderolabs/talos/internal/pkg/capability"
|
||||
"github.com/siderolabs/talos/pkg/conditions"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
)
|
||||
|
||||
// Dashboard implements the Service interface. It serves as the concrete type with
|
||||
// the required methods.
|
||||
type Dashboard struct {
|
||||
TTYNumber int
|
||||
}
|
||||
|
||||
// ID implements the Service interface.
|
||||
func (d *Dashboard) ID(_ runtime.Runtime) string {
|
||||
return "dashboard"
|
||||
}
|
||||
|
||||
// PreFunc implements the Service interface.
|
||||
func (d *Dashboard) PreFunc(_ context.Context, _ runtime.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostFunc implements the Service interface.
|
||||
func (d *Dashboard) PostFunc(_ runtime.Runtime, _ events.ServiceState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Condition implements the Service interface.
|
||||
func (d *Dashboard) Condition(_ runtime.Runtime) conditions.Condition {
|
||||
return conditions.WaitForFileToExist(constants.MachineSocketPath)
|
||||
}
|
||||
|
||||
// DependsOn implements the Service interface.
|
||||
func (d *Dashboard) DependsOn(_ runtime.Runtime) []string {
|
||||
return []string{machinedServiceID}
|
||||
}
|
||||
|
||||
// Runner implements the Service interface.
|
||||
func (d *Dashboard) Runner(r runtime.Runtime) (runner.Runner, error) {
|
||||
tty := fmt.Sprintf("/dev/tty%d", d.TTYNumber)
|
||||
|
||||
return restart.New(process.NewRunner(false, &runner.Args{
|
||||
ID: d.ID(r),
|
||||
ProcessArgs: []string{"/sbin/dashboard"},
|
||||
},
|
||||
runner.WithLoggingManager(r.Logging()),
|
||||
runner.WithEnv([]string{"TERM=linux"}),
|
||||
runner.WithStdinFile(tty),
|
||||
runner.WithStdoutFile(tty),
|
||||
runner.WithCtty(1),
|
||||
runner.WithOOMScoreAdj(-400),
|
||||
runner.WithDroppedCapabilities(capability.AllCapabilitiesSetLowercase()),
|
||||
runner.WithCgroupPath(constants.CgroupDashboard),
|
||||
runner.WithUID(constants.DashboardUserID),
|
||||
),
|
||||
restart.WithType(restart.Forever),
|
||||
), nil
|
||||
}
|
@ -27,6 +27,8 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/role"
|
||||
)
|
||||
|
||||
const machinedServiceID = "machined"
|
||||
|
||||
var rules = map[string]role.Set{
|
||||
"/cluster.ClusterService/HealthCheck": role.MakeSet(role.Admin, role.Reader),
|
||||
|
||||
@ -132,12 +134,12 @@ func (s *machinedService) Main(ctx context.Context, r runtime.Runtime, logWriter
|
||||
)
|
||||
|
||||
// ensure socket dir exists
|
||||
if err := os.MkdirAll(filepath.Dir(constants.MachineSocketPath), 0o750); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(constants.MachineSocketPath), 0o770); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the final leaf to be world-executable to make apid connect to the socket
|
||||
if err := os.Chmod(filepath.Dir(constants.MachineSocketPath), 0o751); err != nil {
|
||||
if err := os.Chmod(filepath.Dir(constants.MachineSocketPath), 0o771); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -176,7 +178,7 @@ type Machined struct {
|
||||
|
||||
// ID implements the Service interface.
|
||||
func (m *Machined) ID(r runtime.Runtime) string {
|
||||
return "machined"
|
||||
return machinedServiceID
|
||||
}
|
||||
|
||||
// PreFunc implements the Service interface.
|
||||
@ -203,7 +205,7 @@ func (m *Machined) DependsOn(r runtime.Runtime) []string {
|
||||
func (m *Machined) Runner(r runtime.Runtime) (runner.Runner, error) {
|
||||
svc := &machinedService{m.Controller}
|
||||
|
||||
return goroutine.NewRunner(r, "machined", svc.Main, runner.WithLoggingManager(r.Logging())), nil
|
||||
return goroutine.NewRunner(r, machinedServiceID, svc.Main, runner.WithLoggingManager(r.Logging())), nil
|
||||
}
|
||||
|
||||
// HealthFunc implements the HealthcheckedService interface.
|
||||
|
@ -28,15 +28,18 @@ var (
|
||||
droppedCaps string
|
||||
cgroupPath string
|
||||
oomScore int
|
||||
uid int
|
||||
)
|
||||
|
||||
// Main is the entrypoint into /sbin/wrapperd.
|
||||
// nolint: gocyclo
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func Main() {
|
||||
flag.StringVar(&name, "name", "", "process name")
|
||||
flag.StringVar(&droppedCaps, "dropped-caps", "", "comma-separated list of capabilities to drop")
|
||||
flag.StringVar(&cgroupPath, "cgroup-path", "", "cgroup path to use")
|
||||
flag.IntVar(&oomScore, "oom-score", 0, "oom score to set")
|
||||
flag.IntVar(&uid, "uid", 0, "uid to set for the process")
|
||||
flag.Parse()
|
||||
|
||||
currentPid := os.Getpid()
|
||||
@ -78,9 +81,9 @@ func Main() {
|
||||
} else if droppedCaps != "" {
|
||||
caps := strings.Split(droppedCaps, ",")
|
||||
dropCaps := slices.Map(caps, func(c string) cap.Value {
|
||||
capability, err := cap.FromName(c)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse capability: %v", err)
|
||||
capability, capErr := cap.FromName(c)
|
||||
if capErr != nil {
|
||||
log.Fatalf("failed to parse capability: %v", capErr)
|
||||
}
|
||||
|
||||
return capability
|
||||
@ -88,15 +91,22 @@ func Main() {
|
||||
|
||||
// drop capabilities
|
||||
iab := cap.IABGetProc()
|
||||
if err := iab.SetVector(cap.Bound, true, dropCaps...); err != nil {
|
||||
if err = iab.SetVector(cap.Bound, true, dropCaps...); err != nil {
|
||||
log.Fatalf("failed to set capabilities: %v", err)
|
||||
}
|
||||
|
||||
if err := iab.SetProc(); err != nil {
|
||||
if err = iab.SetProc(); err != nil {
|
||||
log.Fatalf("failed to apply capabilities: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if uid > 0 {
|
||||
err = unix.Setuid(uid)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to setuid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := unix.Exec(flag.Args()[0], flag.Args()[0:], os.Environ()); err != nil {
|
||||
log.Fatalf("failed to exec: %v", err)
|
||||
}
|
||||
|
@ -8,25 +8,47 @@ package capability
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/siderolabs/gen/maps"
|
||||
"kernel.org/pub/linux/libs/security/libcap/cap"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
)
|
||||
|
||||
// AllGrantableCapabilities returns list of capabilities that can be granted to the container based on
|
||||
// process bounding capabilities.
|
||||
func AllGrantableCapabilities() []string {
|
||||
capabilities := []string{}
|
||||
// AllCapabilitiesSet returns the set of all available capabilities.
|
||||
//
|
||||
// Returned capabilities are in UPPERCASE.
|
||||
func AllCapabilitiesSet() map[string]struct{} {
|
||||
capabilities := make(map[string]struct{})
|
||||
|
||||
for v := cap.Value(0); v < cap.MaxBits(); v++ {
|
||||
if set, _ := cap.GetBound(v); set { //nolint:errcheck
|
||||
if _, ok := constants.DefaultDroppedCapabilities[v.String()]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
capabilities = append(capabilities, strings.ToUpper(v.String()))
|
||||
capabilities[strings.ToUpper(v.String())] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return capabilities
|
||||
}
|
||||
|
||||
// AllCapabilitiesSetLowercase returns the set of all available capabilities.
|
||||
//
|
||||
// Returned capabilities are in lowercase.
|
||||
func AllCapabilitiesSetLowercase() map[string]struct{} {
|
||||
return maps.Map(AllCapabilitiesSet(),
|
||||
func(capability string, _ struct{}) (string, struct{}) {
|
||||
return strings.ToLower(capability), struct{}{}
|
||||
})
|
||||
}
|
||||
|
||||
// AllGrantableCapabilities returns list of capabilities that can be granted to the container based on
|
||||
// process bounding capabilities.
|
||||
//
|
||||
// Returned capabilities are in UPPERCASE.
|
||||
func AllGrantableCapabilities() []string {
|
||||
allCapabilities := AllCapabilitiesSet()
|
||||
|
||||
for dropped := range constants.DefaultDroppedCapabilities {
|
||||
delete(allCapabilities, strings.ToUpper(dropped))
|
||||
}
|
||||
|
||||
return maps.Keys(allCapabilities)
|
||||
}
|
||||
|
66
internal/pkg/console/console.go
Normal file
66
internal/pkg/console/console.go
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 console contains console-related functionality.
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// vtActivate activates the specified virtual terminal.
|
||||
// See VT_ACTIVATE:
|
||||
// https://man7.org/linux/man-pages/man2/ioctl_console.2.html
|
||||
// https://github.com/torvalds/linux/blob/v6.2/include/uapi/linux/vt.h#L42
|
||||
vtActivate uintptr = 0x5606
|
||||
|
||||
// tioclSetKmsgRedirect redirects kernel messages to the specified tty.
|
||||
// See TIOCL_SETKMSGREDIRECT:
|
||||
// https://github.com/torvalds/linux/blob/v6.2/include/uapi/linux/tiocl.h#L33
|
||||
// https://github.com/torvalds/linux/blob/v6.2/drivers/tty/vt/vt.c#L3242
|
||||
tioclSetKmsgRedirect byte = 11
|
||||
)
|
||||
|
||||
// Switch switches the active console to the specified tty.
|
||||
func Switch(ttyNumber int) error {
|
||||
// redirect the kernel logs to tty1 instead of the currently used one,
|
||||
// so that dashboard on tty2 does not get flooded with kernel logs
|
||||
if err := redirectKernelLogs(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we need a valid fd to any tty because ioctl requires it
|
||||
tty0, err := os.OpenFile("/dev/tty0", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer tty0.Close() //nolint: errcheck
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tty0.Fd(), vtActivate, uintptr(ttyNumber)); errno != 0 {
|
||||
return fmt.Errorf("failed to activate console: %w", errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// redirectKernelLogs redirects kernel logs to the specified tty.
|
||||
func redirectKernelLogs(ttyNumber int) error {
|
||||
tty, err := os.OpenFile(fmt.Sprintf("/dev/tty%d", ttyNumber), os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := [2]byte{tioclSetKmsgRedirect, byte(ttyNumber)}
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tty.Fd(), syscall.TIOCLINUX, uintptr(unsafe.Pointer(&args))); errno != 0 {
|
||||
return fmt.Errorf("failed to set redirect for kmsg: %w", errno)
|
||||
}
|
||||
|
||||
return tty.Close()
|
||||
}
|
@ -11,7 +11,7 @@ import (
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// SystemGauges quickly show CPU/mem load.
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"github.com/siderolabs/gen/slices"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// BaseGraph represents the widget with some usage graph.
|
@ -10,7 +10,7 @@ import (
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// LoadAvgInfo represents the widget with load average info.
|
@ -10,7 +10,7 @@ import (
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"github.com/siderolabs/gen/maps"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// NodeTabs represents the bottom bar with node list.
|
@ -8,7 +8,7 @@ import (
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// BaseSparklineGroup represents the widget with some sparklines.
|
@ -15,7 +15,7 @@ import (
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// ProcessTable represents the widget with process info.
|
@ -7,8 +7,8 @@ package components_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/components"
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
"github.com/siderolabs/talos/pkg/machinery/api/machine"
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// TopLine represents the top bar with host info.
|
@ -12,8 +12,8 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
)
|
||||
|
||||
// Main is the entrypoint into talosctl dashboard command.
|
||||
func Main(ctx context.Context, c *client.Client, interval time.Duration) error {
|
||||
// Main is the entrypoint into the dashboard.
|
||||
func Main(ctx context.Context, c *client.Client, interval time.Duration, allowExitKeys bool) error {
|
||||
ui := &UI{}
|
||||
|
||||
source := &APISource{
|
||||
@ -24,5 +24,5 @@ func Main(ctx context.Context, c *client.Client, interval time.Duration) error {
|
||||
dataCh := source.Run(ctx)
|
||||
defer source.Stop()
|
||||
|
||||
return ui.Main(ctx, dataCh)
|
||||
return ui.Main(ctx, dataCh, allowExitKeys)
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
)
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
|
||||
ui "github.com/gizak/termui/v3"
|
||||
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/components"
|
||||
"github.com/siderolabs/talos/cmd/talosctl/cmd/talos/dashboard/data"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/components"
|
||||
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
|
||||
)
|
||||
|
||||
// DataWidget is a widget which consumes Data to draw itself.
|
||||
@ -49,7 +49,7 @@ type UI struct {
|
||||
// Main is the UI entrypoint.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (u *UI) Main(ctx context.Context, dataCh <-chan *data.Data) error {
|
||||
func (u *UI) Main(ctx context.Context, dataCh <-chan *data.Data, allowExitKeys bool) error {
|
||||
if err := ui.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -131,7 +131,9 @@ func (u *UI) Main(ctx context.Context, dataCh <-chan *data.Data) error {
|
||||
case e := <-uiEvents:
|
||||
switch e.ID {
|
||||
case "q", "<C-c>":
|
||||
return nil
|
||||
if allowExitKeys {
|
||||
return nil
|
||||
}
|
||||
case "<Resize>":
|
||||
payload := e.Payload.(ui.Resize) //nolint:errcheck,forcetypeassert
|
||||
|
@ -181,6 +181,10 @@ func RunInstallerContainer(disk, platform, ref string, cfg config.Provider, opts
|
||||
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamEquinixMetalEvents, *c))
|
||||
}
|
||||
|
||||
if c := procfs.ProcCmdline().Get(constants.KernelParamDashboardDisabled).First(); c != nil {
|
||||
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamDashboardDisabled, *c))
|
||||
}
|
||||
|
||||
specOpts := []oci.SpecOpts{
|
||||
oci.WithImageConfig(img),
|
||||
oci.WithProcessArgs(args...),
|
||||
|
@ -49,6 +49,9 @@ const (
|
||||
// cgroups version to use (default is cgroupsv2, setting this kernel arg to '0' forces cgroupsv1).
|
||||
KernelParamCGroups = "talos.unified_cgroup_hierarchy"
|
||||
|
||||
// KernelParamDashboardDisabled is the kernel parameter name for disabling the dashboard.
|
||||
KernelParamDashboardDisabled = "talos.dashboard.disabled"
|
||||
|
||||
// BoardNone indicates that the install is not for a specific board.
|
||||
BoardNone = "none"
|
||||
|
||||
@ -422,6 +425,10 @@ const (
|
||||
// ApidUserID is the user ID for apid.
|
||||
ApidUserID = 50
|
||||
|
||||
// DashboardUserID is the user ID for dashboard.
|
||||
// We use the same user ID as apid so that the dashboard can write to the machined unix socket.
|
||||
DashboardUserID = ApidUserID
|
||||
|
||||
// TrustdPort is the port for the trustd service.
|
||||
TrustdPort = 50001
|
||||
|
||||
@ -546,6 +553,9 @@ const (
|
||||
// CgroupExtensions is the cgroup name for system extension processes.
|
||||
CgroupExtensions = CgroupSystem + "/extensions"
|
||||
|
||||
// CgroupDashboard is the cgroup name for dashboard process.
|
||||
CgroupDashboard = CgroupSystem + "/dashboard"
|
||||
|
||||
// CgroupPodRuntime is the cgroup name for kubernetes containerd runtime processes.
|
||||
CgroupPodRuntime = "/podruntime/runtime"
|
||||
|
||||
@ -558,6 +568,12 @@ const (
|
||||
// CgroupKubeletReservedMemory is the hard memory protection for the kubelet processes.
|
||||
CgroupKubeletReservedMemory = 64 * 1024 * 1024
|
||||
|
||||
// CgroupDashboardReservedMemory is the hard memory protection for the dashboard process.
|
||||
CgroupDashboardReservedMemory = 85 * 1024 * 1024
|
||||
|
||||
// CgroupDashboardLowMemory is the low memory value for the dashboard process.
|
||||
CgroupDashboardLowMemory = 100 * 1024 * 1024
|
||||
|
||||
// FlannelCNI is the string to use Tanos-managed Flannel CNI (default).
|
||||
FlannelCNI = "flannel"
|
||||
|
||||
@ -790,8 +806,14 @@ const (
|
||||
// TrustdMaxProcs is the maximum number of GOMAXPROCS for trustd.
|
||||
TrustdMaxProcs = 2
|
||||
|
||||
// DashboardMaxProcs is the maximum number of GOMAXPROCS for dashboard.
|
||||
DashboardMaxProcs = 2
|
||||
|
||||
// APIAuthzRoleMetadataKey is the gRPC metadata key used to submit a role with os:impersonator.
|
||||
APIAuthzRoleMetadataKey = "talos-role"
|
||||
|
||||
// DashboardTTY is the number of the TTY device (/dev/ttyN) for dashboard.
|
||||
DashboardTTY = 2
|
||||
)
|
||||
|
||||
// See https://linux.die.net/man/3/klogctl
|
||||
|
@ -214,3 +214,13 @@ Talos defaults to always using the unified cgroup hierarchy (`cgroupsv2`), but `
|
||||
can be forced with `talos.unified_cgroup_hierarchy=0`.
|
||||
|
||||
> Note: `cgroupsv1` is deprecated and it should be used only for compatibility with workloads which don't support `cgroupsv2` yet.
|
||||
|
||||
#### `talos.dashboard.disabled`
|
||||
|
||||
By default, Talos redirects kernel logs to virtual console `/dev/tty1` and starts the dashboard on `/dev/tty2`,
|
||||
then switches to the dashboard tty.
|
||||
|
||||
If you set `talos.dashboard.disabled=1`, this behavior will be disabled.
|
||||
Kernel logs will be sent to the currently active console and the dashboard will not be started.
|
||||
|
||||
It is set to be `1` by default on SBCs.
|
||||
|
Loading…
x
Reference in New Issue
Block a user