From 94564891475273ca3dbaccdf32660567d8e8f3fd Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Tue, 13 Feb 2024 20:36:10 +0300 Subject: [PATCH] feat: support hardware watchdog timers Only enabled when activated by config, disabled on shutdown/reboot Fixes #8284 Signed-off-by: Dmitry Sharshakov Signed-off-by: Dmitry Sharshakov Signed-off-by: Andrey Smirnov --- .../definitions/runtime/runtime.proto | 14 + hack/modules-amd64.txt | 10 + hack/release.toml | 16 + .../pkg/controllers/runtime/watchdog_timer.go | 174 +++++++ .../runtime/watchdog_timer_config.go | 84 ++++ .../runtime/watchdog_timer_config_test.go | 60 +++ .../runtime/v1alpha2/v1alpha2_controller.go | 2 + .../pkg/runtime/v1alpha2/v1alpha2_state.go | 2 + .../definitions/runtime/runtime.pb.go | 209 ++++++++- .../definitions/runtime/runtime_vtproto.pb.go | 426 ++++++++++++++++++ pkg/machinery/config/config/runtime.go | 18 +- .../config/schemas/config.schema.json | 46 ++ .../types/runtime/deep_copy.generated.go | 8 +- .../config/types/runtime/event_sink.go | 7 +- .../config/types/runtime/kmsg_log.go | 5 + pkg/machinery/config/types/runtime/runtime.go | 4 +- .../config/types/runtime/runtime_doc.go | 32 ++ .../types/runtime/testdata/watchdogtimer.yaml | 4 + .../config/types/runtime/watchdog_timer.go | 141 ++++++ .../types/runtime/watchdog_timer_test.go | 99 ++++ .../resources/runtime/deep_copy.generated.go | 14 +- pkg/machinery/resources/runtime/runtime.go | 2 +- .../resources/runtime/runtime_test.go | 2 + .../runtime/watchdog_timer_config.go | 72 +++ .../runtime/watchdog_timer_status.go | 70 +++ pkg/provision/providers/qemu/launch.go | 4 + website/content/v1.7/reference/api.md | 35 ++ .../runtime/watchdogtimerconfig.md | 35 ++ .../content/v1.7/schemas/config.schema.json | 46 ++ 29 files changed, 1615 insertions(+), 26 deletions(-) create mode 100644 internal/app/machined/pkg/controllers/runtime/watchdog_timer.go create mode 100644 internal/app/machined/pkg/controllers/runtime/watchdog_timer_config.go create mode 100644 internal/app/machined/pkg/controllers/runtime/watchdog_timer_config_test.go create mode 100644 pkg/machinery/config/types/runtime/testdata/watchdogtimer.yaml create mode 100644 pkg/machinery/config/types/runtime/watchdog_timer.go create mode 100644 pkg/machinery/config/types/runtime/watchdog_timer_test.go create mode 100644 pkg/machinery/resources/runtime/watchdog_timer_config.go create mode 100644 pkg/machinery/resources/runtime/watchdog_timer_status.go create mode 100644 website/content/v1.7/reference/configuration/runtime/watchdogtimerconfig.md diff --git a/api/resource/definitions/runtime/runtime.proto b/api/resource/definitions/runtime/runtime.proto index 956ed83e10..3001e4a3f0 100755 --- a/api/resource/definitions/runtime/runtime.proto +++ b/api/resource/definitions/runtime/runtime.proto @@ -5,6 +5,7 @@ package talos.resource.definitions.runtime; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/runtime"; import "common/common.proto"; +import "google/protobuf/duration.proto"; import "resource/definitions/enums/enums.proto"; // DevicesStatusSpec is the spec for devices status. @@ -126,3 +127,16 @@ message UnmetCondition { string reason = 2; } +// WatchdogTimerConfigSpec describes configuration of watchdog timer. +message WatchdogTimerConfigSpec { + string device = 1; + google.protobuf.Duration timeout = 2; +} + +// WatchdogTimerStatusSpec describes configuration of watchdog timer. +message WatchdogTimerStatusSpec { + string device = 1; + google.protobuf.Duration timeout = 2; + google.protobuf.Duration feed_interval = 3; +} + diff --git a/hack/modules-amd64.txt b/hack/modules-amd64.txt index 0bfd0c0551..0537e1f54a 100644 --- a/hack/modules-amd64.txt +++ b/hack/modules-amd64.txt @@ -11,6 +11,7 @@ kernel/drivers/ata/pata_marvell.ko kernel/drivers/ata/pata_oldpiix.ko kernel/drivers/ata/pata_sch.ko kernel/drivers/block/nbd.ko +kernel/drivers/char/ipmi/ipmi_watchdog.ko kernel/drivers/gpu/drm/amd/amdgpu/amdgpu.ko kernel/drivers/gpu/drm/amd/amdxcp/amdxcp.ko kernel/drivers/gpu/drm/display/drm_display_helper.ko @@ -62,6 +63,8 @@ kernel/drivers/message/fusion/mptbase.ko kernel/drivers/message/fusion/mptsas.ko kernel/drivers/message/fusion/mptscsih.ko kernel/drivers/message/fusion/mptspi.ko +kernel/drivers/mfd/lpc_ich.ko +kernel/drivers/mfd/mfd-core.ko kernel/drivers/misc/hpilo.ko kernel/drivers/mmc/host/sdhci_f_sdh30.ko kernel/drivers/mmc/host/sdhci-acpi.ko @@ -134,6 +137,13 @@ kernel/drivers/virtio/virtio_mmio.ko kernel/drivers/virtio/virtio_pci_legacy_dev.ko kernel/drivers/virtio/virtio_pci_modern_dev.ko kernel/drivers/virtio/virtio_pci.ko +kernel/drivers/watchdog/i6300esb.ko +kernel/drivers/watchdog/iTCO_vendor_support.ko +kernel/drivers/watchdog/iTCO_wdt.ko +kernel/drivers/watchdog/sp5100_tco.ko +kernel/drivers/watchdog/watchdog.ko +kernel/drivers/watchdog/wdat_wdt.ko +kernel/drivers/watchdog/xen_wdt.ko kernel/lib/objagg.ko kernel/lib/parman.ko kernel/lib/raid6/raid6_pq.ko diff --git a/hack/release.toml b/hack/release.toml index 2a91fce695..aaa1b7b31a 100644 --- a/hack/release.toml +++ b/hack/release.toml @@ -161,6 +161,22 @@ machine: title = "CA Rotation" description = """\ Talos Linux now supports rotating the root CA certificate and key for Talos API and Kubernetes API. +""" + + [notes.watchdog] + title = "Hardware Watchdog Timers" + description = """\ +Talos Linux now supports hardware watchdog timers configuration. +If enabled, and the machine becomes unresponsive, the hardware watchdog will reset the machine. + +The watchdog can be enabled with the following configuration document: + +```yaml +apiVersion: v1alpha1 +kind: WatchdogTimerConfig +device: /dev/watchdog0 +timeout: 3m0s +``` """ [make_deps] diff --git a/internal/app/machined/pkg/controllers/runtime/watchdog_timer.go b/internal/app/machined/pkg/controllers/runtime/watchdog_timer.go new file mode 100644 index 0000000000..a95e05710f --- /dev/null +++ b/internal/app/machined/pkg/controllers/runtime/watchdog_timer.go @@ -0,0 +1,174 @@ +// 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 runtime + +import ( + "context" + "fmt" + "os" + "syscall" + "time" + "unsafe" + + "github.com/cosi-project/runtime/pkg/controller" + "github.com/cosi-project/runtime/pkg/safe" + "github.com/cosi-project/runtime/pkg/state" + "github.com/siderolabs/gen/optional" + "go.uber.org/zap" + "golang.org/x/sys/unix" + + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +// WatchdogTimerController watches v1alpha1.Config, creates/updates/deletes kernel module specs. +type WatchdogTimerController struct{} + +// Name implements controller.Controller interface. +func (ctrl *WatchdogTimerController) Name() string { + return "runtime.WatchdogTimerController" +} + +// Inputs implements controller.Controller interface. +func (ctrl *WatchdogTimerController) Inputs() []controller.Input { + return []controller.Input{ + { + Namespace: runtime.NamespaceName, + Type: runtime.WatchdogTimerConfigType, + ID: optional.Some(runtime.WatchdogTimerConfigID), + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *WatchdogTimerController) Outputs() []controller.Output { + return []controller.Output{ + { + Type: runtime.WatchdogTimerStatusType, + Kind: controller.OutputExclusive, + }, + } +} + +// Run implements controller.Controller interface. +// +//nolint:gocyclo,cyclop +func (ctrl *WatchdogTimerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { + var ( + ticker *time.Ticker + tickerC <-chan time.Time + ) + + tickerStop := func() { + if ticker == nil { + return + } + + ticker.Stop() + + ticker = nil + tickerC = nil + } + + defer tickerStop() + + var wd *os.File + + wdClose := func() { + if wd == nil { + return + } + + logger.Info("closing hardware watchdog", zap.String("path", wd.Name())) + + // Magic close: make sure old watchdog won't trip after we close it + if _, err := wd.WriteString("V"); err != nil { + logger.Error("failed to send magic close to watchdog", zap.String("path", wd.Name())) + } + + if err := wd.Close(); err != nil { + logger.Error("failed to close watchdog", zap.String("path", wd.Name())) + } + + wd = nil + } + + defer wdClose() + + for { + select { + case <-ctx.Done(): + return nil + case <-tickerC: + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), unix.WDIOC_KEEPALIVE, 0); err != 0 { + return fmt.Errorf("failed to feed watchdog: %w", err) + } + + continue + case <-r.EventCh(): + } + + cfg, err := safe.ReaderGetByID[*runtime.WatchdogTimerConfig](ctx, r, runtime.WatchdogTimerConfigID) + if err != nil { + if !state.IsNotFoundError(err) { + return fmt.Errorf("error getting watchdog config: %w", err) + } + } + + r.StartTrackingOutputs() + + if cfg == nil { + tickerStop() + wdClose() + } else { + // close the watchdog if requested to use new one + if wd != nil && wd.Name() != cfg.TypedSpec().Device { + wdClose() + } + + if wd == nil { + wd, err = os.OpenFile(cfg.TypedSpec().Device, syscall.O_RDWR, 0o600) + if err != nil { + return fmt.Errorf("failed to open watchdog device: %s", err) + } + + logger.Info("opened hardware watchdog", zap.String("path", cfg.TypedSpec().Device)) + } + + timeout := int(cfg.TypedSpec().Timeout.Seconds()) + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), uintptr(unix.WDIOC_SETTIMEOUT), uintptr(unsafe.Pointer(&timeout))); err != 0 { + return fmt.Errorf("failed to set watchdog timeout: %w", err) + } + + tickerStop() + + // 3 pings per timeout should suffice in any case + feedInterval := cfg.TypedSpec().Timeout / 3 + + ticker = time.NewTicker(feedInterval) + tickerC = ticker.C + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), uintptr(unix.WDIOC_KEEPALIVE), 0); err != 0 { + return fmt.Errorf("failed to feed watchdog: %w", err) + } + + logger.Info("set hardware watchdog timeout", zap.Duration("timeout", cfg.TypedSpec().Timeout), zap.Duration("feed_interval", feedInterval)) + + if err = safe.WriterModify(ctx, r, runtime.NewWatchdogTimerStatus(cfg.Metadata().ID()), func(status *runtime.WatchdogTimerStatus) error { + status.TypedSpec().Device = cfg.TypedSpec().Device + status.TypedSpec().Timeout = cfg.TypedSpec().Timeout + status.TypedSpec().FeedInterval = feedInterval + + return nil + }); err != nil { + return fmt.Errorf("error updating watchdog status: %w", err) + } + } + + if err = safe.CleanupOutputs[*runtime.WatchdogTimerStatus](ctx, r); err != nil { + return err + } + } +} diff --git a/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config.go b/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config.go new file mode 100644 index 0000000000..7a2e969f47 --- /dev/null +++ b/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config.go @@ -0,0 +1,84 @@ +// 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 runtime + +import ( + "context" + "fmt" + + "github.com/cosi-project/runtime/pkg/controller" + "github.com/cosi-project/runtime/pkg/safe" + "github.com/cosi-project/runtime/pkg/state" + "github.com/siderolabs/gen/optional" + "go.uber.org/zap" + + "github.com/siderolabs/talos/pkg/machinery/resources/config" + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +// WatchdogTimerConfigController generates configuration for watchdog timers. +type WatchdogTimerConfigController struct{} + +// Name implements controller.Controller interface. +func (ctrl *WatchdogTimerConfigController) Name() string { + return "runtime.WatchdogTimerConfigController" +} + +// Inputs implements controller.Controller interface. +func (ctrl *WatchdogTimerConfigController) Inputs() []controller.Input { + return []controller.Input{ + { + Namespace: config.NamespaceName, + Type: config.MachineConfigType, + ID: optional.Some(config.V1Alpha1ID), + Kind: controller.InputWeak, + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *WatchdogTimerConfigController) Outputs() []controller.Output { + return []controller.Output{ + { + Type: runtime.WatchdogTimerConfigType, + Kind: controller.OutputExclusive, + }, + } +} + +// Run implements controller.Controller interface. +func (ctrl *WatchdogTimerConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) (err error) { + for { + select { + case <-ctx.Done(): + return nil + case <-r.EventCh(): + } + + cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID) + if err != nil && !state.IsNotFoundError(err) { + return fmt.Errorf("error getting machine config: %w", err) + } + + r.StartTrackingOutputs() + + if cfg != nil { + if watchdogConfig := cfg.Config().Runtime().WatchdogTimer(); watchdogConfig != nil { + if err = safe.WriterModify(ctx, r, runtime.NewWatchdogTimerConfig(), func(cfg *runtime.WatchdogTimerConfig) error { + cfg.TypedSpec().Device = watchdogConfig.Device() + cfg.TypedSpec().Timeout = watchdogConfig.Timeout() + + return nil + }); err != nil { + return fmt.Errorf("error updating kmsg log config: %w", err) + } + } + } + + if err = safe.CleanupOutputs[*runtime.WatchdogTimerConfig](ctx, r); err != nil { + return err + } + } +} diff --git a/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config_test.go b/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config_test.go new file mode 100644 index 0000000000..8a6f209ea4 --- /dev/null +++ b/internal/app/machined/pkg/controllers/runtime/watchdog_timer_config_test.go @@ -0,0 +1,60 @@ +// 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 runtime_test + +import ( + "testing" + + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/resource/rtestutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" + runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" + "github.com/siderolabs/talos/pkg/machinery/config/container" + runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" + "github.com/siderolabs/talos/pkg/machinery/resources/config" + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +type WatchdogTimerConfigSuite struct { + ctest.DefaultSuite +} + +func TestWatchdogTimerConfigSuite(t *testing.T) { + suite.Run(t, new(WatchdogTimerConfigSuite)) +} + +func (suite *WatchdogTimerConfigSuite) TestWatchdogTimerConfigNone() { + suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.WatchdogTimerConfigController{})) + + rtestutils.AssertNoResource[*runtime.WatchdogTimerConfig](suite.Ctx(), suite.T(), suite.State(), runtime.WatchdogTimerConfigID) +} + +func (suite *WatchdogTimerConfigSuite) TestWatchdogTimerConfigMachineConfig() { + suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.WatchdogTimerConfigController{})) + + watchdogTimerConfig := &runtimecfg.WatchdogTimerV1Alpha1{ + WatchdogDevice: "/dev/watchdog0", + } + + cfg, err := container.New(watchdogTimerConfig) + suite.Require().NoError(err) + + suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) + + rtestutils.AssertResources[*runtime.WatchdogTimerConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.WatchdogTimerConfigID}, + func(cfg *runtime.WatchdogTimerConfig, asrt *assert.Assertions) { + asrt.Equal( + "/dev/watchdog0", + cfg.TypedSpec().Device, + ) + asrt.Equal( + runtimecfg.DefaultWatchdogTimeout, + cfg.TypedSpec().Timeout, + ) + }) +} diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index a6772e8ec3..cc6f7aa356 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -305,6 +305,8 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, runtimecontrollers.NewUniqueMachineTokenController(), + &runtimecontrollers.WatchdogTimerConfigController{}, + &runtimecontrollers.WatchdogTimerController{}, &secrets.APICertSANsController{}, &secrets.APIController{}, &secrets.EtcdController{}, diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go index 5f315fc92d..d5597ec3cb 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go @@ -194,6 +194,8 @@ func NewState() (*State, error) { &runtime.PlatformMetadata{}, &runtime.SecurityState{}, &runtime.UniqueMachineToken{}, + &runtime.WatchdogTimerConfig{}, + &runtime.WatchdogTimerStatus{}, &secrets.API{}, &secrets.CertSAN{}, &secrets.Etcd{}, diff --git a/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go b/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go index 8cbaddbb41..620499f03f 100644 --- a/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go +++ b/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go @@ -12,6 +12,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" @@ -1128,6 +1129,126 @@ func (x *UnmetCondition) GetReason() string { return "" } +// WatchdogTimerConfigSpec describes configuration of watchdog timer. +type WatchdogTimerConfigSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *WatchdogTimerConfigSpec) Reset() { + *x = WatchdogTimerConfigSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WatchdogTimerConfigSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchdogTimerConfigSpec) ProtoMessage() {} + +func (x *WatchdogTimerConfigSpec) ProtoReflect() protoreflect.Message { + mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchdogTimerConfigSpec.ProtoReflect.Descriptor instead. +func (*WatchdogTimerConfigSpec) Descriptor() ([]byte, []int) { + return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{19} +} + +func (x *WatchdogTimerConfigSpec) GetDevice() string { + if x != nil { + return x.Device + } + return "" +} + +func (x *WatchdogTimerConfigSpec) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +// WatchdogTimerStatusSpec describes configuration of watchdog timer. +type WatchdogTimerStatusSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + FeedInterval *durationpb.Duration `protobuf:"bytes,3,opt,name=feed_interval,json=feedInterval,proto3" json:"feed_interval,omitempty"` +} + +func (x *WatchdogTimerStatusSpec) Reset() { + *x = WatchdogTimerStatusSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WatchdogTimerStatusSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchdogTimerStatusSpec) ProtoMessage() {} + +func (x *WatchdogTimerStatusSpec) ProtoReflect() protoreflect.Message { + mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchdogTimerStatusSpec.ProtoReflect.Descriptor instead. +func (*WatchdogTimerStatusSpec) Descriptor() ([]byte, []int) { + return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{20} +} + +func (x *WatchdogTimerStatusSpec) GetDevice() string { + if x != nil { + return x.Device + } + return "" +} + +func (x *WatchdogTimerStatusSpec) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +func (x *WatchdogTimerStatusSpec) GetFeedInterval() *durationpb.Duration { + if x != nil { + return x.FeedInterval + } + return nil +} + var File_resource_definitions_runtime_runtime_proto protoreflect.FileDescriptor var file_resource_definitions_runtime_runtime_proto_rawDesc = []byte{ @@ -1137,6 +1258,8 @@ var file_resource_definitions_runtime_runtime_proto_rawDesc = []byte{ 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, 0x0a, @@ -1266,12 +1389,30 @@ var file_resource_definitions_runtime_runtime_proto_rawDesc = []byte{ 0x55, 0x6e, 0x6d, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, - 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x66, 0x0a, 0x17, 0x57, 0x61, + 0x74, 0x63, 0x68, 0x64, 0x6f, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, + 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x17, 0x57, 0x61, 0x74, 0x63, 0x68, 0x64, 0x6f, 0x67, 0x54, + 0x69, 0x6d, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, + 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x66, + 0x65, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x66, + 0x65, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x42, 0x4c, 0x5a, 0x4a, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1286,7 +1427,7 @@ func file_resource_definitions_runtime_runtime_proto_rawDescGZIP() []byte { return file_resource_definitions_runtime_runtime_proto_rawDescData } -var file_resource_definitions_runtime_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_resource_definitions_runtime_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_resource_definitions_runtime_runtime_proto_goTypes = []interface{}{ (*DevicesStatusSpec)(nil), // 0: talos.resource.definitions.runtime.DevicesStatusSpec (*EventSinkConfigSpec)(nil), // 1: talos.resource.definitions.runtime.EventSinkConfigSpec @@ -1307,22 +1448,28 @@ var file_resource_definitions_runtime_runtime_proto_goTypes = []interface{}{ (*SecurityStateSpec)(nil), // 16: talos.resource.definitions.runtime.SecurityStateSpec (*UniqueMachineTokenSpec)(nil), // 17: talos.resource.definitions.runtime.UniqueMachineTokenSpec (*UnmetCondition)(nil), // 18: talos.resource.definitions.runtime.UnmetCondition - (*common.URL)(nil), // 19: common.URL - (enums.RuntimeMachineStage)(0), // 20: talos.resource.definitions.enums.RuntimeMachineStage - (*common.NetIP)(nil), // 21: common.NetIP + (*WatchdogTimerConfigSpec)(nil), // 19: talos.resource.definitions.runtime.WatchdogTimerConfigSpec + (*WatchdogTimerStatusSpec)(nil), // 20: talos.resource.definitions.runtime.WatchdogTimerStatusSpec + (*common.URL)(nil), // 21: common.URL + (enums.RuntimeMachineStage)(0), // 22: talos.resource.definitions.enums.RuntimeMachineStage + (*common.NetIP)(nil), // 23: common.NetIP + (*durationpb.Duration)(nil), // 24: google.protobuf.Duration } var file_resource_definitions_runtime_runtime_proto_depIdxs = []int32{ 2, // 0: talos.resource.definitions.runtime.ExtensionServiceConfigSpec.files:type_name -> talos.resource.definitions.runtime.ExtensionServiceConfigFile - 19, // 1: talos.resource.definitions.runtime.KmsgLogConfigSpec.destinations:type_name -> common.URL - 20, // 2: talos.resource.definitions.runtime.MachineStatusSpec.stage:type_name -> talos.resource.definitions.enums.RuntimeMachineStage + 21, // 1: talos.resource.definitions.runtime.KmsgLogConfigSpec.destinations:type_name -> common.URL + 22, // 2: talos.resource.definitions.runtime.MachineStatusSpec.stage:type_name -> talos.resource.definitions.enums.RuntimeMachineStage 10, // 3: talos.resource.definitions.runtime.MachineStatusSpec.status:type_name -> talos.resource.definitions.runtime.MachineStatusStatus 18, // 4: talos.resource.definitions.runtime.MachineStatusStatus.unmet_conditions:type_name -> talos.resource.definitions.runtime.UnmetCondition - 21, // 5: talos.resource.definitions.runtime.MaintenanceServiceConfigSpec.reachable_addresses:type_name -> common.NetIP - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 23, // 5: talos.resource.definitions.runtime.MaintenanceServiceConfigSpec.reachable_addresses:type_name -> common.NetIP + 24, // 6: talos.resource.definitions.runtime.WatchdogTimerConfigSpec.timeout:type_name -> google.protobuf.Duration + 24, // 7: talos.resource.definitions.runtime.WatchdogTimerStatusSpec.timeout:type_name -> google.protobuf.Duration + 24, // 8: talos.resource.definitions.runtime.WatchdogTimerStatusSpec.feed_interval:type_name -> google.protobuf.Duration + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_resource_definitions_runtime_runtime_proto_init() } @@ -1559,6 +1706,30 @@ func file_resource_definitions_runtime_runtime_proto_init() { return nil } } + file_resource_definitions_runtime_runtime_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WatchdogTimerConfigSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_runtime_runtime_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WatchdogTimerStatusSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1566,7 +1737,7 @@ func file_resource_definitions_runtime_runtime_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_resource_definitions_runtime_runtime_proto_rawDesc, NumEnums: 0, - NumMessages: 19, + NumMessages: 21, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go b/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go index f4cc073e74..f14541efe1 100644 --- a/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go +++ b/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go @@ -9,8 +9,10 @@ import ( io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" + durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb1 "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" @@ -1028,6 +1030,116 @@ func (m *UnmetCondition) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *WatchdogTimerConfigSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WatchdogTimerConfigSpec) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WatchdogTimerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Timeout != nil { + size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WatchdogTimerStatusSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WatchdogTimerStatusSpec) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *WatchdogTimerStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.FeedInterval != nil { + size, err := (*durationpb.Duration)(m.FeedInterval).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.Timeout != nil { + size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *DevicesStatusSpec) SizeVT() (n int) { if m == nil { return 0 @@ -1409,6 +1521,46 @@ func (m *UnmetCondition) SizeVT() (n int) { return n } +func (m *WatchdogTimerConfigSpec) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Timeout != nil { + l = (*durationpb.Duration)(m.Timeout).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *WatchdogTimerStatusSpec) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Timeout != nil { + l = (*durationpb.Duration)(m.Timeout).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.FeedInterval != nil { + l = (*durationpb.Duration)(m.FeedInterval).SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *DevicesStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3673,3 +3825,277 @@ func (m *UnmetCondition) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *WatchdogTimerConfigSpec) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WatchdogTimerConfigSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WatchdogTimerConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timeout == nil { + m.Timeout = &durationpb1.Duration{} + } + if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WatchdogTimerStatusSpec) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WatchdogTimerStatusSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WatchdogTimerStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Timeout == nil { + m.Timeout = &durationpb1.Duration{} + } + if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeedInterval", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FeedInterval == nil { + m.FeedInterval = &durationpb1.Duration{} + } + if err := (*durationpb.Duration)(m.FeedInterval).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/pkg/machinery/config/config/runtime.go b/pkg/machinery/config/config/runtime.go index 3f75581087..2814646993 100644 --- a/pkg/machinery/config/config/runtime.go +++ b/pkg/machinery/config/config/runtime.go @@ -4,12 +4,22 @@ package config -import "net/url" +import ( + "net/url" + "time" +) // RuntimeConfig defines the interface to access Talos runtime configuration. type RuntimeConfig interface { EventsEndpoint() *string KmsgLogURLs() []*url.URL + WatchdogTimer() WatchdogTimerConfig +} + +// WatchdogTimerConfig defines the interface to access Talos watchdog timer configuration. +type WatchdogTimerConfig interface { + Device() string + Timeout() time.Duration } // WrapRuntimeConfigList wraps a list of RuntimeConfig into a single RuntimeConfig aggregating the results. @@ -30,3 +40,9 @@ func (w runtimeConfigWrapper) KmsgLogURLs() []*url.URL { return c.KmsgLogURLs() }) } + +func (w runtimeConfigWrapper) WatchdogTimer() WatchdogTimerConfig { + return findFirstValue(w, func(c RuntimeConfig) WatchdogTimerConfig { + return c.WatchdogTimer() + }) +} diff --git a/pkg/machinery/config/schemas/config.schema.json b/pkg/machinery/config/schemas/config.schema.json index ef02cd7495..d9b4d4d8ed 100644 --- a/pkg/machinery/config/schemas/config.schema.json +++ b/pkg/machinery/config/schemas/config.schema.json @@ -304,6 +304,49 @@ "kind" ] }, + "runtime.WatchdogTimerV1Alpha1": { + "properties": { + "apiVersion": { + "enum": [ + "v1alpha1" + ], + "title": "apiVersion", + "description": "apiVersion is the API version of the resource.\n", + "markdownDescription": "apiVersion is the API version of the resource.", + "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" + }, + "kind": { + "enum": [ + "WatchdogTimerConfig" + ], + "title": "kind", + "description": "kind is the kind of the resource.\n", + "markdownDescription": "kind is the kind of the resource.", + "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" + }, + "device": { + "type": "string", + "title": "device", + "description": "Path to the watchdog device.\n", + "markdownDescription": "Path to the watchdog device.", + "x-intellij-html-description": "\u003cp\u003ePath to the watchdog device.\u003c/p\u003e\n" + }, + "timeout": { + "type": "string", + "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", + "title": "timeout", + "description": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n", + "markdownDescription": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", + "x-intellij-html-description": "\u003cp\u003eTimeout for the watchdog.\u003c/p\u003e\n\n\u003cp\u003eIf Talos is unresponsive for this duration, the watchdog will reset the system.\u003c/p\u003e\n\n\u003cp\u003eDefault value is 1 minute, minimum value is 10 seconds.\u003c/p\u003e\n" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "apiVersion", + "kind" + ] + }, "siderolink.ConfigV1Alpha1": { "properties": { "apiVersion": { @@ -3308,6 +3351,9 @@ { "$ref": "#/$defs/runtime.KmsgLogV1Alpha1" }, + { + "$ref": "#/$defs/runtime.WatchdogTimerV1Alpha1" + }, { "$ref": "#/$defs/siderolink.ConfigV1Alpha1" }, diff --git a/pkg/machinery/config/types/runtime/deep_copy.generated.go b/pkg/machinery/config/types/runtime/deep_copy.generated.go index 2602ecb28a..e33a93050f 100644 --- a/pkg/machinery/config/types/runtime/deep_copy.generated.go +++ b/pkg/machinery/config/types/runtime/deep_copy.generated.go @@ -2,7 +2,7 @@ // 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/. -// Code generated by "deep-copy -type EventSinkV1Alpha1 -type KmsgLogV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. +// Code generated by "deep-copy -type EventSinkV1Alpha1 -type KmsgLogV1Alpha1 -type WatchdogTimerV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package runtime @@ -29,3 +29,9 @@ func (o *KmsgLogV1Alpha1) DeepCopy() *KmsgLogV1Alpha1 { } return &cp } + +// DeepCopy generates a deep copy of *WatchdogTimerV1Alpha1. +func (o *WatchdogTimerV1Alpha1) DeepCopy() *WatchdogTimerV1Alpha1 { + var cp WatchdogTimerV1Alpha1 = *o + return &cp +} diff --git a/pkg/machinery/config/types/runtime/event_sink.go b/pkg/machinery/config/types/runtime/event_sink.go index 64e20d63f7..9ab35185c3 100644 --- a/pkg/machinery/config/types/runtime/event_sink.go +++ b/pkg/machinery/config/types/runtime/event_sink.go @@ -25,7 +25,7 @@ const EventSinkKind = "EventSinkConfig" func init() { registry.Register(EventSinkKind, func(version string) config.Document { switch version { - case "v1alpha1": + case "v1alpha1": //nolint:goconst return &EventSinkV1Alpha1{} default: return nil @@ -93,6 +93,11 @@ func (s *EventSinkV1Alpha1) KmsgLogURLs() []*url.URL { return nil } +// WatchdogTimer implements config.RuntimeConfig interface. +func (s *EventSinkV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { + return nil +} + // Validate implements config.Validator interface. func (s *EventSinkV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { _, _, err := net.SplitHostPort(s.Endpoint) diff --git a/pkg/machinery/config/types/runtime/kmsg_log.go b/pkg/machinery/config/types/runtime/kmsg_log.go index 70deb5a4c3..21070524e1 100644 --- a/pkg/machinery/config/types/runtime/kmsg_log.go +++ b/pkg/machinery/config/types/runtime/kmsg_log.go @@ -108,6 +108,11 @@ func (s *KmsgLogV1Alpha1) KmsgLogURLs() []*url.URL { return []*url.URL{s.KmsgLogURL.URL} } +// WatchdogTimer implements config.RuntimeConfig interface. +func (s *KmsgLogV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { + return nil +} + // Validate implements config.Validator interface. func (s *KmsgLogV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.MetaName == "" { diff --git a/pkg/machinery/config/types/runtime/runtime.go b/pkg/machinery/config/types/runtime/runtime.go index d3a93b7848..d41cf40ee2 100644 --- a/pkg/machinery/config/types/runtime/runtime.go +++ b/pkg/machinery/config/types/runtime/runtime.go @@ -5,6 +5,6 @@ // Package runtime provides runtime machine configuration documents. package runtime -//go:generate docgen -output runtime_doc.go runtime.go kmsg_log.go event_sink.go +//go:generate docgen -output runtime_doc.go runtime.go kmsg_log.go event_sink.go watchdog_timer.go -//go:generate deep-copy -type EventSinkV1Alpha1 -type KmsgLogV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . +//go:generate deep-copy -type EventSinkV1Alpha1 -type KmsgLogV1Alpha1 -type WatchdogTimerV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . diff --git a/pkg/machinery/config/types/runtime/runtime_doc.go b/pkg/machinery/config/types/runtime/runtime_doc.go index b11bae8a54..4c297bbadc 100644 --- a/pkg/machinery/config/types/runtime/runtime_doc.go +++ b/pkg/machinery/config/types/runtime/runtime_doc.go @@ -64,6 +64,37 @@ func (EventSinkV1Alpha1) Doc() *encoder.Doc { return doc } +func (WatchdogTimerV1Alpha1) Doc() *encoder.Doc { + doc := &encoder.Doc{ + Type: "WatchdogTimerConfig", + Comments: [3]string{"" /* encoder.HeadComment */, "WatchdogTimerConfig is a watchdog timer config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, + Description: "WatchdogTimerConfig is a watchdog timer config document.", + Fields: []encoder.Doc{ + {}, + { + Name: "device", + Type: "string", + Note: "", + Description: "Path to the watchdog device.", + Comments: [3]string{"" /* encoder.HeadComment */, "Path to the watchdog device." /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, + { + Name: "timeout", + Type: "Duration", + Note: "", + Description: "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", + Comments: [3]string{"" /* encoder.HeadComment */, "Timeout for the watchdog." /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, + }, + } + + doc.AddExample("", exampleWatchdogTimerV1Alpha1()) + + doc.Fields[1].AddExample("", "/dev/watchdog0") + + return doc +} + // GetFileDoc returns documentation for the file runtime_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ @@ -72,6 +103,7 @@ func GetFileDoc() *encoder.FileDoc { Structs: []*encoder.Doc{ KmsgLogV1Alpha1{}.Doc(), EventSinkV1Alpha1{}.Doc(), + WatchdogTimerV1Alpha1{}.Doc(), }, } } diff --git a/pkg/machinery/config/types/runtime/testdata/watchdogtimer.yaml b/pkg/machinery/config/types/runtime/testdata/watchdogtimer.yaml new file mode 100644 index 0000000000..ea46514a38 --- /dev/null +++ b/pkg/machinery/config/types/runtime/testdata/watchdogtimer.yaml @@ -0,0 +1,4 @@ +apiVersion: v1alpha1 +kind: WatchdogTimerConfig +device: /dev/watchdog0 +timeout: 3m0s diff --git a/pkg/machinery/config/types/runtime/watchdog_timer.go b/pkg/machinery/config/types/runtime/watchdog_timer.go new file mode 100644 index 0000000000..0e0e47af9c --- /dev/null +++ b/pkg/machinery/config/types/runtime/watchdog_timer.go @@ -0,0 +1,141 @@ +// 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 runtime + +//docgen:jsonschema + +import ( + "fmt" + "net/url" + "time" + + "github.com/siderolabs/talos/pkg/machinery/config/config" + "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" + "github.com/siderolabs/talos/pkg/machinery/config/types/meta" + "github.com/siderolabs/talos/pkg/machinery/config/validation" +) + +// WatchdogTimerKind is a watchdog timer config document kind. +const WatchdogTimerKind = "WatchdogTimerConfig" + +func init() { + registry.Register(WatchdogTimerKind, func(version string) config.Document { + switch version { + case "v1alpha1": + return &WatchdogTimerV1Alpha1{} + default: + return nil + } + }) +} + +// Check interfaces. +var ( + _ config.RuntimeConfig = &WatchdogTimerV1Alpha1{} + _ config.Validator = &WatchdogTimerV1Alpha1{} +) + +// Timeout constants. +const ( + MinWatchdogTimeout = 10 * time.Second + DefaultWatchdogTimeout = time.Minute +) + +// WatchdogTimerV1Alpha1 is a watchdog timer config document. +// +// examples: +// - value: exampleWatchdogTimerV1Alpha1() +// alias: WatchdogTimerConfig +// schemaRoot: true +// schemaMeta: v1alpha1/WatchdogTimerConfig +type WatchdogTimerV1Alpha1 struct { + meta.Meta `yaml:",inline"` + // description: | + // Path to the watchdog device. + // examples: + // - value: > + // "/dev/watchdog0" + WatchdogDevice string `yaml:"device"` + // description: | + // Timeout for the watchdog. + // + // If Talos is unresponsive for this duration, the watchdog will reset the system. + // + // Default value is 1 minute, minimum value is 10 seconds. + // schema: + // type: string + // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ + WatchdogTimeout time.Duration `yaml:"timeout,omitempty"` +} + +// NewWatchdogTimerV1Alpha1 creates a new eventsink config document. +func NewWatchdogTimerV1Alpha1() *WatchdogTimerV1Alpha1 { + return &WatchdogTimerV1Alpha1{ + Meta: meta.Meta{ + MetaKind: WatchdogTimerKind, + MetaAPIVersion: "v1alpha1", + }, + } +} + +func exampleWatchdogTimerV1Alpha1() *WatchdogTimerV1Alpha1 { + cfg := NewWatchdogTimerV1Alpha1() + cfg.WatchdogDevice = "/dev/watchdog0" + cfg.WatchdogTimeout = 2 * time.Minute + + return cfg +} + +// Clone implements config.Document interface. +func (s *WatchdogTimerV1Alpha1) Clone() config.Document { + return s.DeepCopy() +} + +// Runtime implements config.Config interface. +func (s *WatchdogTimerV1Alpha1) Runtime() config.RuntimeConfig { + return s +} + +// EventsEndpoint implements config.RuntimeConfig interface. +func (s *WatchdogTimerV1Alpha1) EventsEndpoint() *string { + return nil +} + +// KmsgLogURLs implements config.RuntimeConfig interface. +func (s *WatchdogTimerV1Alpha1) KmsgLogURLs() []*url.URL { + return nil +} + +// WatchdogTimer implements config.RuntimeConfig interface. +func (s *WatchdogTimerV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { + return s +} + +// Device implements config.WatchdogTimerConfig interface. +func (s *WatchdogTimerV1Alpha1) Device() string { + return s.WatchdogDevice +} + +// Timeout implements config.WatchdogTimerConfig interface. +func (s *WatchdogTimerV1Alpha1) Timeout() time.Duration { + if s.WatchdogTimeout == 0 { + return DefaultWatchdogTimeout + } + + return s.WatchdogTimeout +} + +// Validate implements config.Validator interface. +func (s *WatchdogTimerV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { + if s.WatchdogDevice == "" { + return nil, fmt.Errorf("watchdog device: empty value") + } + + if s.WatchdogTimeout != 0 && s.WatchdogTimeout < MinWatchdogTimeout { + return nil, fmt.Errorf("watchdog timeout: minimum value is %s", MinWatchdogTimeout) + } + + return nil, nil +} diff --git a/pkg/machinery/config/types/runtime/watchdog_timer_test.go b/pkg/machinery/config/types/runtime/watchdog_timer_test.go new file mode 100644 index 0000000000..666454b029 --- /dev/null +++ b/pkg/machinery/config/types/runtime/watchdog_timer_test.go @@ -0,0 +1,99 @@ +// 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 runtime_test + +import ( + _ "embed" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/siderolabs/talos/pkg/machinery/config/encoder" + "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" +) + +//go:embed testdata/watchdogtimer.yaml +var expectedWatchdogTimerDocument []byte + +func TestWatchdogTimerMarshalStability(t *testing.T) { + cfg := runtime.NewWatchdogTimerV1Alpha1() + cfg.WatchdogDevice = "/dev/watchdog0" + cfg.WatchdogTimeout = 3 * time.Minute + + marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() + require.NoError(t, err) + + t.Log(string(marshaled)) + + assert.Equal(t, expectedWatchdogTimerDocument, marshaled) +} + +func TestWatchdogTimerValidate(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + name string + cfg func() *runtime.WatchdogTimerV1Alpha1 + + expectedError string + expectedWarnings []string + }{ + { + name: "empty", + cfg: runtime.NewWatchdogTimerV1Alpha1, + + expectedError: "watchdog device: empty value", + }, + { + name: "negative timeout", + cfg: func() *runtime.WatchdogTimerV1Alpha1 { + cfg := runtime.NewWatchdogTimerV1Alpha1() + cfg.WatchdogDevice = "/dev/watchdog1" + cfg.WatchdogTimeout = -1 * time.Minute + + return cfg + }, + + expectedError: "watchdog timeout: minimum value is 10s", + }, + { + name: "small timeout", + cfg: func() *runtime.WatchdogTimerV1Alpha1 { + cfg := runtime.NewWatchdogTimerV1Alpha1() + cfg.WatchdogDevice = "/dev/watchdog1" + cfg.WatchdogTimeout = time.Second + + return cfg + }, + + expectedError: "watchdog timeout: minimum value is 10s", + }, + { + name: "valid", + cfg: func() *runtime.WatchdogTimerV1Alpha1 { + cfg := runtime.NewWatchdogTimerV1Alpha1() + cfg.WatchdogDevice = "/dev/watchdog2" + + return cfg + }, + }, + } { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + warnings, err := test.cfg().Validate(validationMode{}) + + assert.Equal(t, test.expectedWarnings, warnings) + + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/pkg/machinery/resources/runtime/deep_copy.generated.go b/pkg/machinery/resources/runtime/deep_copy.generated.go index 5d999e130a..8a837f29a2 100644 --- a/pkg/machinery/resources/runtime/deep_copy.generated.go +++ b/pkg/machinery/resources/runtime/deep_copy.generated.go @@ -2,7 +2,7 @@ // 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/. -// Code generated by "deep-copy -type DevicesStatusSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type UniqueMachineTokenSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. +// Code generated by "deep-copy -type DevicesStatusSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type UniqueMachineTokenSpec -type WatchdogTimerConfigSpec -type WatchdogTimerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package runtime @@ -160,3 +160,15 @@ func (o UniqueMachineTokenSpec) DeepCopy() UniqueMachineTokenSpec { var cp UniqueMachineTokenSpec = o return cp } + +// DeepCopy generates a deep copy of WatchdogTimerConfigSpec. +func (o WatchdogTimerConfigSpec) DeepCopy() WatchdogTimerConfigSpec { + var cp WatchdogTimerConfigSpec = o + return cp +} + +// DeepCopy generates a deep copy of WatchdogTimerStatusSpec. +func (o WatchdogTimerStatusSpec) DeepCopy() WatchdogTimerStatusSpec { + var cp WatchdogTimerStatusSpec = o + return cp +} diff --git a/pkg/machinery/resources/runtime/runtime.go b/pkg/machinery/resources/runtime/runtime.go index 20af3eb6a4..8916e210e6 100644 --- a/pkg/machinery/resources/runtime/runtime.go +++ b/pkg/machinery/resources/runtime/runtime.go @@ -10,7 +10,7 @@ import ( "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) -//go:generate deep-copy -type DevicesStatusSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type UniqueMachineTokenSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . +//go:generate deep-copy -type DevicesStatusSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type UniqueMachineTokenSpec -type WatchdogTimerConfigSpec -type WatchdogTimerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains configuration resources. const NamespaceName resource.Namespace = v1alpha1.NamespaceName diff --git a/pkg/machinery/resources/runtime/runtime_test.go b/pkg/machinery/resources/runtime/runtime_test.go index 2f007f625e..1d246a43a4 100644 --- a/pkg/machinery/resources/runtime/runtime_test.go +++ b/pkg/machinery/resources/runtime/runtime_test.go @@ -42,6 +42,8 @@ func TestRegisterResource(t *testing.T) { &runtime.PlatformMetadata{}, &runtime.SecurityState{}, &runtime.UniqueMachineToken{}, + &runtime.WatchdogTimerConfig{}, + &runtime.WatchdogTimerStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } diff --git a/pkg/machinery/resources/runtime/watchdog_timer_config.go b/pkg/machinery/resources/runtime/watchdog_timer_config.go new file mode 100644 index 0000000000..ef7db43f66 --- /dev/null +++ b/pkg/machinery/resources/runtime/watchdog_timer_config.go @@ -0,0 +1,72 @@ +// 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 runtime + +import ( + "time" + + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/resource/meta" + "github.com/cosi-project/runtime/pkg/resource/protobuf" + "github.com/cosi-project/runtime/pkg/resource/typed" + + "github.com/siderolabs/talos/pkg/machinery/proto" +) + +// WatchdogTimerConfigType is type of WatchdogTimerConfig resource. +const WatchdogTimerConfigType = resource.Type("WatchdogTimerConfigs.runtime.talos.dev") + +// WatchdogTimerConfig resource holds configuration for watchdog timer. +type WatchdogTimerConfig = typed.Resource[WatchdogTimerConfigSpec, WatchdogTimerConfigExtension] + +// WatchdogTimerConfigID is a resource ID for WatchdogTimerConfig. +const WatchdogTimerConfigID resource.ID = "timer" + +// WatchdogTimerConfigSpec describes configuration of watchdog timer. +// +//gotagsrewrite:gen +type WatchdogTimerConfigSpec struct { + Device string `yaml:"device" protobuf:"1"` + Timeout time.Duration `yaml:"timeout" protobuf:"2"` +} + +// NewWatchdogTimerConfig initializes a WatchdogTimerConfig resource. +func NewWatchdogTimerConfig() *WatchdogTimerConfig { + return typed.NewResource[WatchdogTimerConfigSpec, WatchdogTimerConfigExtension]( + resource.NewMetadata(NamespaceName, WatchdogTimerConfigType, WatchdogTimerConfigID, resource.VersionUndefined), + WatchdogTimerConfigSpec{}, + ) +} + +// WatchdogTimerConfigExtension is auxiliary resource data for WatchdogTimerConfig. +type WatchdogTimerConfigExtension struct{} + +// ResourceDefinition implements meta.ResourceDefinitionProvider interface. +func (WatchdogTimerConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { + return meta.ResourceDefinitionSpec{ + Type: WatchdogTimerConfigType, + Aliases: []resource.Type{}, + DefaultNamespace: NamespaceName, + PrintColumns: []meta.PrintColumn{ + { + Name: "Device", + JSONPath: `{.device}`, + }, + { + Name: "Timeout", + JSONPath: `{.timeout}`, + }, + }, + } +} + +func init() { + proto.RegisterDefaultTypes() + + err := protobuf.RegisterDynamic[WatchdogTimerConfigSpec](WatchdogTimerConfigType, &WatchdogTimerConfig{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/runtime/watchdog_timer_status.go b/pkg/machinery/resources/runtime/watchdog_timer_status.go new file mode 100644 index 0000000000..3dd9aaa08d --- /dev/null +++ b/pkg/machinery/resources/runtime/watchdog_timer_status.go @@ -0,0 +1,70 @@ +// 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 runtime + +import ( + "time" + + "github.com/cosi-project/runtime/pkg/resource" + "github.com/cosi-project/runtime/pkg/resource/meta" + "github.com/cosi-project/runtime/pkg/resource/protobuf" + "github.com/cosi-project/runtime/pkg/resource/typed" + + "github.com/siderolabs/talos/pkg/machinery/proto" +) + +// WatchdogTimerStatusType is type of WatchdogTimerStatus resource. +const WatchdogTimerStatusType = resource.Type("WatchdogTimerStatuses.runtime.talos.dev") + +// WatchdogTimerStatus resource holds status of watchdog timer. +type WatchdogTimerStatus = typed.Resource[WatchdogTimerStatusSpec, WatchdogTimerStatusExtension] + +// WatchdogTimerStatusSpec describes configuration of watchdog timer. +// +//gotagsrewrite:gen +type WatchdogTimerStatusSpec struct { + Device string `yaml:"device" protobuf:"1"` + Timeout time.Duration `yaml:"timeout" protobuf:"2"` + FeedInterval time.Duration `yaml:"feedInterval" protobuf:"3"` +} + +// NewWatchdogTimerStatus initializes a WatchdogTimerStatus resource. +func NewWatchdogTimerStatus(id string) *WatchdogTimerStatus { + return typed.NewResource[WatchdogTimerStatusSpec, WatchdogTimerStatusExtension]( + resource.NewMetadata(NamespaceName, WatchdogTimerStatusType, id, resource.VersionUndefined), + WatchdogTimerStatusSpec{}, + ) +} + +// WatchdogTimerStatusExtension is auxiliary resource data for WatchdogTimerStatus. +type WatchdogTimerStatusExtension struct{} + +// ResourceDefinition implements meta.ResourceDefinitionProvider interface. +func (WatchdogTimerStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { + return meta.ResourceDefinitionSpec{ + Type: WatchdogTimerStatusType, + Aliases: []resource.Type{}, + DefaultNamespace: NamespaceName, + PrintColumns: []meta.PrintColumn{ + { + Name: "Device", + JSONPath: `{.device}`, + }, + { + Name: "Timeout", + JSONPath: `{.timeout}`, + }, + }, + } +} + +func init() { + proto.RegisterDefaultTypes() + + err := protobuf.RegisterDynamic[WatchdogTimerStatusSpec](WatchdogTimerStatusType, &WatchdogTimerStatus{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/provision/providers/qemu/launch.go b/pkg/provision/providers/qemu/launch.go index 53cdbf7889..eb98e9d32c 100644 --- a/pkg/provision/providers/qemu/launch.go +++ b/pkg/provision/providers/qemu/launch.go @@ -326,6 +326,10 @@ func launchVM(config *LaunchConfig) error { "virtio-serial", "-device", "virtserialport,chardev=qga0,name=org.qemu.guest_agent.0", + "-device", + "i6300esb,id=watchdog0", + "-watchdog-action", + "pause", } for i, disk := range config.DiskPaths { diff --git a/website/content/v1.7/reference/api.md b/website/content/v1.7/reference/api.md index 2bd72e3975..613c67ba27 100644 --- a/website/content/v1.7/reference/api.md +++ b/website/content/v1.7/reference/api.md @@ -231,6 +231,8 @@ description: Talos gRPC API reference. - [SecurityStateSpec](#talos.resource.definitions.runtime.SecurityStateSpec) - [UniqueMachineTokenSpec](#talos.resource.definitions.runtime.UniqueMachineTokenSpec) - [UnmetCondition](#talos.resource.definitions.runtime.UnmetCondition) + - [WatchdogTimerConfigSpec](#talos.resource.definitions.runtime.WatchdogTimerConfigSpec) + - [WatchdogTimerStatusSpec](#talos.resource.definitions.runtime.WatchdogTimerStatusSpec) - [resource/definitions/secrets/secrets.proto](#resource/definitions/secrets/secrets.proto) - [APICertsSpec](#talos.resource.definitions.secrets.APICertsSpec) @@ -4202,6 +4204,39 @@ UnmetCondition is a failure which prevents machine from being ready at the stage + + + +### WatchdogTimerConfigSpec +WatchdogTimerConfigSpec describes configuration of watchdog timer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| device | [string](#string) | | | +| timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | + + + + + + + + +### WatchdogTimerStatusSpec +WatchdogTimerStatusSpec describes configuration of watchdog timer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| device | [string](#string) | | | +| timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | +| feed_interval | [google.protobuf.Duration](#google.protobuf.Duration) | | | + + + + + diff --git a/website/content/v1.7/reference/configuration/runtime/watchdogtimerconfig.md b/website/content/v1.7/reference/configuration/runtime/watchdogtimerconfig.md new file mode 100644 index 0000000000..151ff91129 --- /dev/null +++ b/website/content/v1.7/reference/configuration/runtime/watchdogtimerconfig.md @@ -0,0 +1,35 @@ +--- +description: WatchdogTimerConfig is a watchdog timer config document. +title: WatchdogTimerConfig +--- + + + + + + + + + + + +{{< highlight yaml >}} +apiVersion: v1alpha1 +kind: WatchdogTimerConfig +device: /dev/watchdog0 # Path to the watchdog device. +timeout: 2m0s # Timeout for the watchdog. +{{< /highlight >}} + + +| Field | Type | Description | Value(s) | +|-------|------|-------------|----------| +|`device` |string |Path to the watchdog device.
Show example(s){{< highlight yaml >}} +device: /dev/watchdog0 +{{< /highlight >}}
| | +|`timeout` |Duration |
Timeout for the watchdog.
If Talos is unresponsive for this duration, the watchdog will reset the system.

Default value is 1 minute, minimum value is 10 seconds.
| | + + + + + + diff --git a/website/content/v1.7/schemas/config.schema.json b/website/content/v1.7/schemas/config.schema.json index ef02cd7495..d9b4d4d8ed 100644 --- a/website/content/v1.7/schemas/config.schema.json +++ b/website/content/v1.7/schemas/config.schema.json @@ -304,6 +304,49 @@ "kind" ] }, + "runtime.WatchdogTimerV1Alpha1": { + "properties": { + "apiVersion": { + "enum": [ + "v1alpha1" + ], + "title": "apiVersion", + "description": "apiVersion is the API version of the resource.\n", + "markdownDescription": "apiVersion is the API version of the resource.", + "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" + }, + "kind": { + "enum": [ + "WatchdogTimerConfig" + ], + "title": "kind", + "description": "kind is the kind of the resource.\n", + "markdownDescription": "kind is the kind of the resource.", + "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" + }, + "device": { + "type": "string", + "title": "device", + "description": "Path to the watchdog device.\n", + "markdownDescription": "Path to the watchdog device.", + "x-intellij-html-description": "\u003cp\u003ePath to the watchdog device.\u003c/p\u003e\n" + }, + "timeout": { + "type": "string", + "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", + "title": "timeout", + "description": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n", + "markdownDescription": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", + "x-intellij-html-description": "\u003cp\u003eTimeout for the watchdog.\u003c/p\u003e\n\n\u003cp\u003eIf Talos is unresponsive for this duration, the watchdog will reset the system.\u003c/p\u003e\n\n\u003cp\u003eDefault value is 1 minute, minimum value is 10 seconds.\u003c/p\u003e\n" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "apiVersion", + "kind" + ] + }, "siderolink.ConfigV1Alpha1": { "properties": { "apiVersion": { @@ -3308,6 +3351,9 @@ { "$ref": "#/$defs/runtime.KmsgLogV1Alpha1" }, + { + "$ref": "#/$defs/runtime.WatchdogTimerV1Alpha1" + }, { "$ref": "#/$defs/siderolink.ConfigV1Alpha1" },