chore: provide a resource to peek into Linux clock adjustments

This is a follow-up for #7567, which won't be backported to 1.5.

This allows to get an output like:

```
$ talosctl -n 172.20.0.5 get adjtimestatus -w
NODE         *   NAMESPACE TYPE            ID     VERSION   OFFSET        ESTERROR   MAXERROR   STATUS               SYNC
172.20.0.5   +   runtime   AdjtimeStatus   node   47        -18.14306ms   0s         191.5ms    STA_PLL | STA_NANO   true
172.20.0.5       runtime   AdjtimeStatus   node   48        -17.109555ms  0s         206.5ms    STA_NANO | STA_PLL   true
172.20.0.5       runtime   AdjtimeStatus   node   49        -16.134923ms  0s         221.5ms    STA_NANO | STA_PLL   true
172.20.0.5       runtime   AdjtimeStatus   node   50        -15.21581ms   0s         236.5ms    STA_PLL | STA_NANO   true
```

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2023-08-03 21:53:47 +04:00
parent 4eab3017b0
commit 0f1920bdda
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
12 changed files with 901 additions and 27 deletions

View File

@ -4,6 +4,20 @@ package talos.resource.definitions.time;
option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/time";
import "google/protobuf/duration.proto";
// AdjtimeStatusSpec describes Linux internal adjtime state.
message AdjtimeStatusSpec {
google.protobuf.Duration offset = 1;
double frequency_adjustment_ratio = 2;
google.protobuf.Duration max_error = 3;
google.protobuf.Duration est_error = 4;
string status = 5;
int64 constant = 6;
bool sync_status = 7;
string state = 8;
}
// StatusSpec describes time sync state.
message StatusSpec {
bool synced = 1;

View File

@ -0,0 +1,97 @@
// 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 time
import (
"context"
"fmt"
stdtime "time"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/safe"
"go.uber.org/zap"
"golang.org/x/sys/unix"
v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
"github.com/siderolabs/talos/internal/pkg/timex"
"github.com/siderolabs/talos/pkg/machinery/resources/time"
)
// AdjtimeStatusController manages time.AdjtimeStatus based on Linux kernel info.
type AdjtimeStatusController struct {
V1Alpha1Mode v1alpha1runtime.Mode
}
// Name implements controller.Controller interface.
func (ctrl *AdjtimeStatusController) Name() string {
return "time.AdjtimeStatusController"
}
// Inputs implements controller.Controller interface.
func (ctrl *AdjtimeStatusController) Inputs() []controller.Input {
return nil
}
// Outputs implements controller.Controller interface.
func (ctrl *AdjtimeStatusController) Outputs() []controller.Output {
return []controller.Output{
{
Type: time.AdjtimeStatusType,
Kind: controller.OutputExclusive,
},
}
}
// Run implements controller.Controller interface.
func (ctrl *AdjtimeStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer {
// in container mode, clock is managed by the host
return nil
}
const pollInterval = 30 * stdtime.Second
pollTicker := stdtime.NewTicker(pollInterval)
defer pollTicker.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
case <-pollTicker.C:
}
var timexBuf unix.Timex
state, err := timex.Adjtimex(&timexBuf)
if err != nil {
return fmt.Errorf("failed to get adjtimex state: %w", err)
}
scale := stdtime.Nanosecond
if timexBuf.Status&unix.STA_NANO == 0 {
scale = stdtime.Microsecond
}
if err := safe.WriterModify(ctx, r, time.NewAdjtimeStatus(), func(status *time.AdjtimeStatus) error {
status.TypedSpec().Offset = stdtime.Duration(timexBuf.Offset) * scale //nolint:durationcheck
status.TypedSpec().FrequencyAdjustmentRatio = 1 + float64(timexBuf.Freq)/65536.0/1000000.0
status.TypedSpec().MaxError = stdtime.Duration(timexBuf.Maxerror) * stdtime.Microsecond //nolint:durationcheck
status.TypedSpec().EstError = stdtime.Duration(timexBuf.Esterror) * stdtime.Microsecond //nolint:durationcheck
status.TypedSpec().Status = timex.Status(timexBuf.Status).String()
status.TypedSpec().State = state.String()
status.TypedSpec().Constant = int(timexBuf.Constant)
status.TypedSpec().SyncStatus = timexBuf.Status&unix.STA_UNSYNC == 0
return nil
}); err != nil {
return fmt.Errorf("failed to update adjtime status: %w", err)
}
r.ResetRestartBackoff()
}
}

View File

@ -299,6 +299,9 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
Cmdline: procfs.ProcCmdline(),
},
&siderolink.ManagerController{},
&timecontrollers.AdjtimeStatusController{
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
},
&timecontrollers.SyncController{
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
},

View File

@ -196,6 +196,7 @@ func NewState() (*State, error) {
&secrets.OSRoot{},
&secrets.Trustd{},
&siderolink.Config{},
&time.AdjtimeStatus{},
&time.Status{},
&v1alpha1.AcquireConfigSpec{},
&v1alpha1.AcquireConfigStatus{},

View File

@ -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"
)
const (
@ -21,6 +22,110 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// AdjtimeStatusSpec describes Linux internal adjtime state.
type AdjtimeStatusSpec struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Offset *durationpb.Duration `protobuf:"bytes,1,opt,name=offset,proto3" json:"offset,omitempty"`
FrequencyAdjustmentRatio float64 `protobuf:"fixed64,2,opt,name=frequency_adjustment_ratio,json=frequencyAdjustmentRatio,proto3" json:"frequency_adjustment_ratio,omitempty"`
MaxError *durationpb.Duration `protobuf:"bytes,3,opt,name=max_error,json=maxError,proto3" json:"max_error,omitempty"`
EstError *durationpb.Duration `protobuf:"bytes,4,opt,name=est_error,json=estError,proto3" json:"est_error,omitempty"`
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
Constant int64 `protobuf:"varint,6,opt,name=constant,proto3" json:"constant,omitempty"`
SyncStatus bool `protobuf:"varint,7,opt,name=sync_status,json=syncStatus,proto3" json:"sync_status,omitempty"`
State string `protobuf:"bytes,8,opt,name=state,proto3" json:"state,omitempty"`
}
func (x *AdjtimeStatusSpec) Reset() {
*x = AdjtimeStatusSpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_time_time_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AdjtimeStatusSpec) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AdjtimeStatusSpec) ProtoMessage() {}
func (x *AdjtimeStatusSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_time_time_proto_msgTypes[0]
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 AdjtimeStatusSpec.ProtoReflect.Descriptor instead.
func (*AdjtimeStatusSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_time_time_proto_rawDescGZIP(), []int{0}
}
func (x *AdjtimeStatusSpec) GetOffset() *durationpb.Duration {
if x != nil {
return x.Offset
}
return nil
}
func (x *AdjtimeStatusSpec) GetFrequencyAdjustmentRatio() float64 {
if x != nil {
return x.FrequencyAdjustmentRatio
}
return 0
}
func (x *AdjtimeStatusSpec) GetMaxError() *durationpb.Duration {
if x != nil {
return x.MaxError
}
return nil
}
func (x *AdjtimeStatusSpec) GetEstError() *durationpb.Duration {
if x != nil {
return x.EstError
}
return nil
}
func (x *AdjtimeStatusSpec) GetStatus() string {
if x != nil {
return x.Status
}
return ""
}
func (x *AdjtimeStatusSpec) GetConstant() int64 {
if x != nil {
return x.Constant
}
return 0
}
func (x *AdjtimeStatusSpec) GetSyncStatus() bool {
if x != nil {
return x.SyncStatus
}
return false
}
func (x *AdjtimeStatusSpec) GetState() string {
if x != nil {
return x.State
}
return ""
}
// StatusSpec describes time sync state.
type StatusSpec struct {
state protoimpl.MessageState
@ -35,7 +140,7 @@ type StatusSpec struct {
func (x *StatusSpec) Reset() {
*x = StatusSpec{}
if protoimpl.UnsafeEnabled {
mi := &file_resource_definitions_time_time_proto_msgTypes[0]
mi := &file_resource_definitions_time_time_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -48,7 +153,7 @@ func (x *StatusSpec) String() string {
func (*StatusSpec) ProtoMessage() {}
func (x *StatusSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_time_time_proto_msgTypes[0]
mi := &file_resource_definitions_time_time_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -61,7 +166,7 @@ func (x *StatusSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use StatusSpec.ProtoReflect.Descriptor instead.
func (*StatusSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_time_time_proto_rawDescGZIP(), []int{0}
return file_resource_definitions_time_time_proto_rawDescGZIP(), []int{1}
}
func (x *StatusSpec) GetSynced() bool {
@ -92,18 +197,42 @@ var file_resource_definitions_time_time_proto_rawDesc = []byte{
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x74, 0x61, 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, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x5f, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x12, 0x14, 0x0a,
0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x70,
0x6f, 0x63, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x64, 0x69, 0x73, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63,
0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x49, 0x5a, 0x47, 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, 0x74,
0x69, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6e, 0x73, 0x2e, 0x74, 0x69, 0x6d, 0x65, 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, 0x22, 0xdf, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x6a, 0x74,
0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a,
0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 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, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x12, 0x3c, 0x0a, 0x1a, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x61, 0x64,
0x6a, 0x75, 0x73, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x02,
0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x41,
0x64, 0x6a, 0x75, 0x73, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x12, 0x36,
0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 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, 0x08, 0x6d, 0x61,
0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x36, 0x0a, 0x09, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x04, 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, 0x08, 0x65, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x16,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75,
0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x5f, 0x0a, 0x0a, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6e, 0x63, 0x65,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x12,
0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x64, 0x69,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x79,
0x6e, 0x63, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x49, 0x5a, 0x47, 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, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -118,16 +247,21 @@ func file_resource_definitions_time_time_proto_rawDescGZIP() []byte {
return file_resource_definitions_time_time_proto_rawDescData
}
var file_resource_definitions_time_time_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_resource_definitions_time_time_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_resource_definitions_time_time_proto_goTypes = []interface{}{
(*StatusSpec)(nil), // 0: talos.resource.definitions.time.StatusSpec
(*AdjtimeStatusSpec)(nil), // 0: talos.resource.definitions.time.AdjtimeStatusSpec
(*StatusSpec)(nil), // 1: talos.resource.definitions.time.StatusSpec
(*durationpb.Duration)(nil), // 2: google.protobuf.Duration
}
var file_resource_definitions_time_time_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
2, // 0: talos.resource.definitions.time.AdjtimeStatusSpec.offset:type_name -> google.protobuf.Duration
2, // 1: talos.resource.definitions.time.AdjtimeStatusSpec.max_error:type_name -> google.protobuf.Duration
2, // 2: talos.resource.definitions.time.AdjtimeStatusSpec.est_error:type_name -> google.protobuf.Duration
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_resource_definitions_time_time_proto_init() }
@ -137,6 +271,18 @@ func file_resource_definitions_time_time_proto_init() {
}
if !protoimpl.UnsafeEnabled {
file_resource_definitions_time_time_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AdjtimeStatusSpec); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_resource_definitions_time_time_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StatusSpec); i {
case 0:
return &v.state
@ -155,7 +301,7 @@ func file_resource_definitions_time_time_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_resource_definitions_time_time_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -5,11 +5,15 @@
package time
import (
binary "encoding/binary"
fmt "fmt"
io "io"
math "math"
bits "math/bits"
proto "google.golang.org/protobuf/proto"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
)
const (
@ -19,6 +23,140 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
func (m *AdjtimeStatusSpec) 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 *AdjtimeStatusSpec) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *AdjtimeStatusSpec) 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 len(m.State) > 0 {
i -= len(m.State)
copy(dAtA[i:], m.State)
i = encodeVarint(dAtA, i, uint64(len(m.State)))
i--
dAtA[i] = 0x42
}
if m.SyncStatus {
i--
if m.SyncStatus {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x38
}
if m.Constant != 0 {
i = encodeVarint(dAtA, i, uint64(m.Constant))
i--
dAtA[i] = 0x30
}
if len(m.Status) > 0 {
i -= len(m.Status)
copy(dAtA[i:], m.Status)
i = encodeVarint(dAtA, i, uint64(len(m.Status)))
i--
dAtA[i] = 0x2a
}
if m.EstError != nil {
if vtmsg, ok := interface{}(m.EstError).(interface {
MarshalToSizedBufferVT([]byte) (int, error)
}); ok {
size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
} else {
encoded, err := proto.Marshal(m.EstError)
if err != nil {
return 0, err
}
i -= len(encoded)
copy(dAtA[i:], encoded)
i = encodeVarint(dAtA, i, uint64(len(encoded)))
}
i--
dAtA[i] = 0x22
}
if m.MaxError != nil {
if vtmsg, ok := interface{}(m.MaxError).(interface {
MarshalToSizedBufferVT([]byte) (int, error)
}); ok {
size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
} else {
encoded, err := proto.Marshal(m.MaxError)
if err != nil {
return 0, err
}
i -= len(encoded)
copy(dAtA[i:], encoded)
i = encodeVarint(dAtA, i, uint64(len(encoded)))
}
i--
dAtA[i] = 0x1a
}
if m.FrequencyAdjustmentRatio != 0 {
i -= 8
binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.FrequencyAdjustmentRatio))))
i--
dAtA[i] = 0x11
}
if m.Offset != nil {
if vtmsg, ok := interface{}(m.Offset).(interface {
MarshalToSizedBufferVT([]byte) (int, error)
}); ok {
size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarint(dAtA, i, uint64(size))
} else {
encoded, err := proto.Marshal(m.Offset)
if err != nil {
return 0, err
}
i -= len(encoded)
copy(dAtA[i:], encoded)
i = encodeVarint(dAtA, i, uint64(len(encoded)))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *StatusSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -88,6 +226,63 @@ func encodeVarint(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
return base
}
func (m *AdjtimeStatusSpec) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Offset != nil {
if size, ok := interface{}(m.Offset).(interface {
SizeVT() int
}); ok {
l = size.SizeVT()
} else {
l = proto.Size(m.Offset)
}
n += 1 + l + sov(uint64(l))
}
if m.FrequencyAdjustmentRatio != 0 {
n += 9
}
if m.MaxError != nil {
if size, ok := interface{}(m.MaxError).(interface {
SizeVT() int
}); ok {
l = size.SizeVT()
} else {
l = proto.Size(m.MaxError)
}
n += 1 + l + sov(uint64(l))
}
if m.EstError != nil {
if size, ok := interface{}(m.EstError).(interface {
SizeVT() int
}); ok {
l = size.SizeVT()
} else {
l = proto.Size(m.EstError)
}
n += 1 + l + sov(uint64(l))
}
l = len(m.Status)
if l > 0 {
n += 1 + l + sov(uint64(l))
}
if m.Constant != 0 {
n += 1 + sov(uint64(m.Constant))
}
if m.SyncStatus {
n += 2
}
l = len(m.State)
if l > 0 {
n += 1 + l + sov(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *StatusSpec) SizeVT() (n int) {
if m == nil {
return 0
@ -113,6 +308,303 @@ func sov(x uint64) (n int) {
func soz(x uint64) (n int) {
return sov(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *AdjtimeStatusSpec) 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 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: AdjtimeStatusSpec: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AdjtimeStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Offset == nil {
m.Offset = &durationpb.Duration{}
}
if unmarshal, ok := interface{}(m.Offset).(interface {
UnmarshalVT([]byte) error
}); ok {
if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
} else {
if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Offset); err != nil {
return err
}
}
iNdEx = postIndex
case 2:
if wireType != 1 {
return fmt.Errorf("proto: wrong wireType = %d for field FrequencyAdjustmentRatio", wireType)
}
var v uint64
if (iNdEx + 8) > l {
return io.ErrUnexpectedEOF
}
v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:]))
iNdEx += 8
m.FrequencyAdjustmentRatio = float64(math.Float64frombits(v))
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MaxError", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.MaxError == nil {
m.MaxError = &durationpb.Duration{}
}
if unmarshal, ok := interface{}(m.MaxError).(interface {
UnmarshalVT([]byte) error
}); ok {
if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
} else {
if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.MaxError); err != nil {
return err
}
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field EstError", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.EstError == nil {
m.EstError = &durationpb.Duration{}
}
if unmarshal, ok := interface{}(m.EstError).(interface {
UnmarshalVT([]byte) error
}); ok {
if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
} else {
if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.EstError); err != nil {
return err
}
}
iNdEx = postIndex
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 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 ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Status = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Constant", wireType)
}
m.Constant = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Constant |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field SyncStatus", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.SyncStatus = bool(v != 0)
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field State", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 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 ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.State = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return 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 *StatusSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -0,0 +1,91 @@
// 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 time
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"
"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
)
// AdjtimeStatusType is type of AdjtimeStatus resource.
const AdjtimeStatusType = resource.Type("AdjtimeStatuses.v1alpha1.talos.dev")
// AdjtimeStatusID is the ID of the singletone resource.
const AdjtimeStatusID = resource.ID("node")
// AdjtimeStatus describes running current time sync AdjtimeStatus.
type AdjtimeStatus = typed.Resource[AdjtimeStatusSpec, AdjtimeStatusExtension]
// AdjtimeStatusSpec describes Linux internal adjtime state.
//
//gotagsrewrite:gen
type AdjtimeStatusSpec struct {
Offset time.Duration `yaml:"offset" protobuf:"1"`
FrequencyAdjustmentRatio float64 `yaml:"frequencyAdjustmentRatio" protobuf:"2"`
MaxError time.Duration `yaml:"maxError" protobuf:"3"`
EstError time.Duration `yaml:"estError" protobuf:"4"`
Status string `yaml:"status" protobuf:"5"`
Constant int `yaml:"constant" protobuf:"6"`
SyncStatus bool `yaml:"syncStatus" protobuf:"7"`
State string `yaml:"state" protobuf:"8"`
}
// NewAdjtimeStatus initializes a TimeSync resource.
func NewAdjtimeStatus() *AdjtimeStatus {
return typed.NewResource[AdjtimeStatusSpec, AdjtimeStatusExtension](
resource.NewMetadata(v1alpha1.NamespaceName, AdjtimeStatusType, AdjtimeStatusID, resource.VersionUndefined),
AdjtimeStatusSpec{},
)
}
// AdjtimeStatusExtension provides auxiliary methods for AdjtimeStatus.
type AdjtimeStatusExtension struct{}
// ResourceDefinition implements meta.ResourceDefinitionProvider interface.
func (AdjtimeStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec {
return meta.ResourceDefinitionSpec{
Type: AdjtimeStatusType,
Aliases: []resource.Type{},
DefaultNamespace: v1alpha1.NamespaceName,
PrintColumns: []meta.PrintColumn{
{
Name: "Offset",
JSONPath: "{.offset}",
},
{
Name: "EstError",
JSONPath: "{.estError}",
},
{
Name: "MaxError",
JSONPath: "{.maxError}",
},
{
Name: "Status",
JSONPath: "{.status}",
},
{
Name: "Sync",
JSONPath: "{.syncStatus}",
},
},
}
}
func init() {
proto.RegisterDefaultTypes()
err := protobuf.RegisterDynamic[AdjtimeStatusSpec](AdjtimeStatusType, &AdjtimeStatus{})
if err != nil {
panic(err)
}
}

View File

@ -2,10 +2,16 @@
// 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 StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
// Code generated by "deep-copy -type AdjtimeStatusSpec -type StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
package time
// DeepCopy generates a deep copy of AdjtimeStatusSpec.
func (o AdjtimeStatusSpec) DeepCopy() AdjtimeStatusSpec {
var cp AdjtimeStatusSpec = o
return cp
}
// DeepCopy generates a deep copy of StatusSpec.
func (o StatusSpec) DeepCopy() StatusSpec {
var cp StatusSpec = o

View File

@ -14,9 +14,6 @@ import (
"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
)
//nolint:lll
//go:generate deep-copy -type StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
// StatusType is type of TimeSync resource.
const StatusType = resource.Type("TimeStatuses.v1alpha1.talos.dev")

View File

@ -4,3 +4,6 @@
// Package time provides time-related resources.
package time
//nolint:lll
//go:generate deep-copy -type AdjtimeStatusSpec -type StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .

View File

@ -25,6 +25,7 @@ func TestRegisterResource(t *testing.T) {
resourceRegistry := registry.NewResourceRegistry(resources)
for _, resource := range []meta.ResourceWithRD{
&time.AdjtimeStatus{},
&time.Status{},
} {
assert.NoError(t, resourceRegistry.Register(ctx, resource))

View File

@ -221,6 +221,7 @@ description: Talos gRPC API reference.
- [ConfigSpec](#talos.resource.definitions.siderolink.ConfigSpec)
- [resource/definitions/time/time.proto](#resource/definitions/time/time.proto)
- [AdjtimeStatusSpec](#talos.resource.definitions.time.AdjtimeStatusSpec)
- [StatusSpec](#talos.resource.definitions.time.StatusSpec)
- [resource/definitions/v1alpha1/v1alpha1.proto](#resource/definitions/v1alpha1/v1alpha1.proto)
@ -3952,6 +3953,28 @@ ConfigSpec describes KubeSpan configuration..
<a name="talos.resource.definitions.time.AdjtimeStatusSpec"></a>
### AdjtimeStatusSpec
AdjtimeStatusSpec describes Linux internal adjtime state.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| offset | [google.protobuf.Duration](#google.protobuf.Duration) | | |
| frequency_adjustment_ratio | [double](#double) | | |
| max_error | [google.protobuf.Duration](#google.protobuf.Duration) | | |
| est_error | [google.protobuf.Duration](#google.protobuf.Duration) | | |
| status | [string](#string) | | |
| constant | [int64](#int64) | | |
| sync_status | [bool](#bool) | | |
| state | [string](#string) | | |
<a name="talos.resource.definitions.time.StatusSpec"></a>
### StatusSpec