feat: check on start up if the account has Talos < 1.6 and strict tokens
Some checks failed
default / default (push) Has been cancelled
default / e2e-backups (push) Has been cancelled
default / e2e-cluster-import (push) Has been cancelled
default / e2e-forced-removal (push) Has been cancelled
default / e2e-omni-upgrade (push) Has been cancelled
default / e2e-scaling (push) Has been cancelled
default / e2e-short (push) Has been cancelled
default / e2e-short-secureboot (push) Has been cancelled
default / e2e-templates (push) Has been cancelled
default / e2e-upgrades (push) Has been cancelled
default / e2e-workload-proxy (push) Has been cancelled
default / integration-test (push) Has been cancelled
default / lint (push) Has been cancelled
default / unit-tests (push) Has been cancelled

If both conditions are true, make Omni crash with the error explaining
that using Talos 1.6 and `strict` join tokens mode is not possible.

Fixes: https://github.com/siderolabs/omni/issues/1588

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit is contained in:
Artem Chernyshev 2025-09-19 18:15:06 +03:00
parent 99f1506f01
commit dbe39ea1fc
No known key found for this signature in database
GPG Key ID: 9BAC0E08F5067BB8
12 changed files with 391 additions and 72 deletions

View File

@ -5868,6 +5868,7 @@ type MachineStatusMetricsSpec struct {
ConnectedMachinesCount uint32 `protobuf:"varint,2,opt,name=connected_machines_count,json=connectedMachinesCount,proto3" json:"connected_machines_count,omitempty"`
AllocatedMachinesCount uint32 `protobuf:"varint,3,opt,name=allocated_machines_count,json=allocatedMachinesCount,proto3" json:"allocated_machines_count,omitempty"`
PendingMachinesCount uint32 `protobuf:"varint,4,opt,name=pending_machines_count,json=pendingMachinesCount,proto3" json:"pending_machines_count,omitempty"`
VersionsMap map[string]int32 `protobuf:"bytes,5,rep,name=versions_map,json=versionsMap,proto3" json:"versions_map,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -5930,6 +5931,13 @@ func (x *MachineStatusMetricsSpec) GetPendingMachinesCount() uint32 {
return 0
}
func (x *MachineStatusMetricsSpec) GetVersionsMap() map[string]int32 {
if x != nil {
return x.VersionsMap
}
return nil
}
// ClusterStatusMetricsSpec provides aggregated state of the number of clusters in not ready phase.
// and the number of clusters in each phase.
type ClusterStatusMetricsSpec struct {
@ -8688,7 +8696,7 @@ type ClusterDiagnosticsSpec_Node struct {
func (x *ClusterDiagnosticsSpec_Node) Reset() {
*x = ClusterDiagnosticsSpec_Node{}
mi := &file_omni_specs_omni_proto_msgTypes[118]
mi := &file_omni_specs_omni_proto_msgTypes[119]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -8700,7 +8708,7 @@ func (x *ClusterDiagnosticsSpec_Node) String() string {
func (*ClusterDiagnosticsSpec_Node) ProtoMessage() {}
func (x *ClusterDiagnosticsSpec_Node) ProtoReflect() protoreflect.Message {
mi := &file_omni_specs_omni_proto_msgTypes[118]
mi := &file_omni_specs_omni_proto_msgTypes[119]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -8742,7 +8750,7 @@ type InfraMachineBMCConfigSpec_IPMI struct {
func (x *InfraMachineBMCConfigSpec_IPMI) Reset() {
*x = InfraMachineBMCConfigSpec_IPMI{}
mi := &file_omni_specs_omni_proto_msgTypes[119]
mi := &file_omni_specs_omni_proto_msgTypes[120]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -8754,7 +8762,7 @@ func (x *InfraMachineBMCConfigSpec_IPMI) String() string {
func (*InfraMachineBMCConfigSpec_IPMI) ProtoMessage() {}
func (x *InfraMachineBMCConfigSpec_IPMI) ProtoReflect() protoreflect.Message {
mi := &file_omni_specs_omni_proto_msgTypes[119]
mi := &file_omni_specs_omni_proto_msgTypes[120]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -8807,7 +8815,7 @@ type InfraMachineBMCConfigSpec_API struct {
func (x *InfraMachineBMCConfigSpec_API) Reset() {
*x = InfraMachineBMCConfigSpec_API{}
mi := &file_omni_specs_omni_proto_msgTypes[120]
mi := &file_omni_specs_omni_proto_msgTypes[121]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -8819,7 +8827,7 @@ func (x *InfraMachineBMCConfigSpec_API) String() string {
func (*InfraMachineBMCConfigSpec_API) ProtoMessage() {}
func (x *InfraMachineBMCConfigSpec_API) ProtoReflect() protoreflect.Message {
mi := &file_omni_specs_omni_proto_msgTypes[120]
mi := &file_omni_specs_omni_proto_msgTypes[121]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -8853,7 +8861,7 @@ type InfraProviderCombinedStatusSpec_Health struct {
func (x *InfraProviderCombinedStatusSpec_Health) Reset() {
*x = InfraProviderCombinedStatusSpec_Health{}
mi := &file_omni_specs_omni_proto_msgTypes[121]
mi := &file_omni_specs_omni_proto_msgTypes[122]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -8865,7 +8873,7 @@ func (x *InfraProviderCombinedStatusSpec_Health) String() string {
func (*InfraProviderCombinedStatusSpec_Health) ProtoMessage() {}
func (x *InfraProviderCombinedStatusSpec_Health) ProtoReflect() protoreflect.Message {
mi := &file_omni_specs_omni_proto_msgTypes[121]
mi := &file_omni_specs_omni_proto_msgTypes[122]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -9478,12 +9486,16 @@ const file_omni_specs_omni_proto_rawDesc = "" +
"\tInstalled\x10\x00\x12\x0e\n" +
"\n" +
"Installing\x10\x01\x12\f\n" +
"\bRemoving\x10\x02\"\x80\x02\n" +
"\bRemoving\x10\x02\"\x95\x03\n" +
"\x18MachineStatusMetricsSpec\x12:\n" +
"\x19registered_machines_count\x18\x01 \x01(\rR\x17registeredMachinesCount\x128\n" +
"\x18connected_machines_count\x18\x02 \x01(\rR\x16connectedMachinesCount\x128\n" +
"\x18allocated_machines_count\x18\x03 \x01(\rR\x16allocatedMachinesCount\x124\n" +
"\x16pending_machines_count\x18\x04 \x01(\rR\x14pendingMachinesCount\"\xc2\x01\n" +
"\x16pending_machines_count\x18\x04 \x01(\rR\x14pendingMachinesCount\x12S\n" +
"\fversions_map\x18\x05 \x03(\v20.specs.MachineStatusMetricsSpec.VersionsMapEntryR\vversionsMap\x1a>\n" +
"\x10VersionsMapEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01\"\xc2\x01\n" +
"\x18ClusterStatusMetricsSpec\x12&\n" +
"\x0fnot_ready_count\x18\x01 \x01(\rR\rnotReadyCount\x12C\n" +
"\x06phases\x18\x02 \x03(\v2+.specs.ClusterStatusMetricsSpec.PhasesEntryR\x06phases\x1a9\n" +
@ -9612,7 +9624,7 @@ func file_omni_specs_omni_proto_rawDescGZIP() []byte {
}
var file_omni_specs_omni_proto_enumTypes = make([]protoimpl.EnumInfo, 22)
var file_omni_specs_omni_proto_msgTypes = make([]protoimpl.MessageInfo, 122)
var file_omni_specs_omni_proto_msgTypes = make([]protoimpl.MessageInfo, 123)
var file_omni_specs_omni_proto_goTypes = []any{
(ConfigApplyStatus)(0), // 0: specs.ConfigApplyStatus
(MachineSetPhase)(0), // 1: specs.MachineSetPhase
@ -9753,14 +9765,15 @@ var file_omni_specs_omni_proto_goTypes = []any{
(*ImagePullRequestSpec_NodeImageList)(nil), // 136: specs.ImagePullRequestSpec.NodeImageList
(*TalosExtensionsSpec_Info)(nil), // 137: specs.TalosExtensionsSpec.Info
(*MachineExtensionsStatusSpec_Item)(nil), // 138: specs.MachineExtensionsStatusSpec.Item
nil, // 139: specs.ClusterStatusMetricsSpec.PhasesEntry
(*ClusterDiagnosticsSpec_Node)(nil), // 140: specs.ClusterDiagnosticsSpec.Node
(*InfraMachineBMCConfigSpec_IPMI)(nil), // 141: specs.InfraMachineBMCConfigSpec.IPMI
(*InfraMachineBMCConfigSpec_API)(nil), // 142: specs.InfraMachineBMCConfigSpec.API
(*InfraProviderCombinedStatusSpec_Health)(nil), // 143: specs.InfraProviderCombinedStatusSpec.Health
(*durationpb.Duration)(nil), // 144: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 145: google.protobuf.Timestamp
(*machine.MachineStatusEvent)(nil), // 146: machine.MachineStatusEvent
nil, // 139: specs.MachineStatusMetricsSpec.VersionsMapEntry
nil, // 140: specs.ClusterStatusMetricsSpec.PhasesEntry
(*ClusterDiagnosticsSpec_Node)(nil), // 141: specs.ClusterDiagnosticsSpec.Node
(*InfraMachineBMCConfigSpec_IPMI)(nil), // 142: specs.InfraMachineBMCConfigSpec.IPMI
(*InfraMachineBMCConfigSpec_API)(nil), // 143: specs.InfraMachineBMCConfigSpec.API
(*InfraProviderCombinedStatusSpec_Health)(nil), // 144: specs.InfraProviderCombinedStatusSpec.Health
(*durationpb.Duration)(nil), // 145: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 146: google.protobuf.Timestamp
(*machine.MachineStatusEvent)(nil), // 147: machine.MachineStatusEvent
}
var file_omni_specs_omni_proto_depIdxs = []int32{
111, // 0: specs.MachineStatusSpec.hardware:type_name -> specs.MachineStatusSpec.HardwareStatus
@ -9774,13 +9787,13 @@ var file_omni_specs_omni_proto_depIdxs = []int32{
23, // 8: specs.MachineStatusSpec.security_state:type_name -> specs.SecurityState
121, // 9: specs.ClusterSpec.features:type_name -> specs.ClusterSpec.Features
30, // 10: specs.ClusterSpec.backup_configuration:type_name -> specs.EtcdBackupConf
144, // 11: specs.EtcdBackupConf.interval:type_name -> google.protobuf.Duration
145, // 12: specs.EtcdBackupSpec.created_at:type_name -> google.protobuf.Timestamp
144, // 13: specs.BackupDataSpec.interval:type_name -> google.protobuf.Duration
145, // 11: specs.EtcdBackupConf.interval:type_name -> google.protobuf.Duration
146, // 12: specs.EtcdBackupSpec.created_at:type_name -> google.protobuf.Timestamp
145, // 13: specs.BackupDataSpec.interval:type_name -> google.protobuf.Duration
6, // 14: specs.EtcdBackupStatusSpec.status:type_name -> specs.EtcdBackupStatusSpec.Status
145, // 15: specs.EtcdBackupStatusSpec.last_backup_time:type_name -> google.protobuf.Timestamp
145, // 16: specs.EtcdBackupStatusSpec.last_backup_attempt:type_name -> google.protobuf.Timestamp
145, // 17: specs.EtcdManualBackupSpec.backup_at:type_name -> google.protobuf.Timestamp
146, // 15: specs.EtcdBackupStatusSpec.last_backup_time:type_name -> google.protobuf.Timestamp
146, // 16: specs.EtcdBackupStatusSpec.last_backup_attempt:type_name -> google.protobuf.Timestamp
146, // 17: specs.EtcdManualBackupSpec.backup_at:type_name -> google.protobuf.Timestamp
36, // 18: specs.EtcdBackupOverallStatusSpec.last_backup_status:type_name -> specs.EtcdBackupStatusSpec
7, // 19: specs.ClusterMachineStatusSpec.stage:type_name -> specs.ClusterMachineStatusSpec.Stage
0, // 20: specs.ClusterMachineStatusSpec.config_apply_status:type_name -> specs.ConfigApplyStatus
@ -9798,7 +9811,7 @@ var file_omni_specs_omni_proto_depIdxs = []int32{
1, // 32: specs.MachineSetStatusSpec.phase:type_name -> specs.MachineSetPhase
48, // 33: specs.MachineSetStatusSpec.machines:type_name -> specs.Machines
124, // 34: specs.MachineSetStatusSpec.machine_allocation:type_name -> specs.MachineSetSpec.MachineAllocation
146, // 35: specs.MachineStatusSnapshotSpec.machine_status:type_name -> machine.MachineStatusEvent
147, // 35: specs.MachineStatusSnapshotSpec.machine_status:type_name -> machine.MachineStatusEvent
13, // 36: specs.MachineStatusSnapshotSpec.power_stage:type_name -> specs.MachineStatusSnapshotSpec.PowerStage
128, // 37: specs.ControlPlaneStatusSpec.conditions:type_name -> specs.ControlPlaneStatusSpec.Condition
129, // 38: specs.KubernetesStatusSpec.nodes:type_name -> specs.KubernetesStatusSpec.NodeStatus
@ -9809,9 +9822,9 @@ var file_omni_specs_omni_proto_depIdxs = []int32{
73, // 43: specs.OngoingTaskSpec.destroy:type_name -> specs.DestroyStatusSpec
80, // 44: specs.FeaturesConfigSpec.etcd_backup_settings:type_name -> specs.EtcdBackupSettings
79, // 45: specs.FeaturesConfigSpec.user_pilot_settings:type_name -> specs.UserPilotSettings
144, // 46: specs.EtcdBackupSettings.tick_interval:type_name -> google.protobuf.Duration
144, // 47: specs.EtcdBackupSettings.min_interval:type_name -> google.protobuf.Duration
144, // 48: specs.EtcdBackupSettings.max_interval:type_name -> google.protobuf.Duration
145, // 46: specs.EtcdBackupSettings.tick_interval:type_name -> google.protobuf.Duration
145, // 47: specs.EtcdBackupSettings.min_interval:type_name -> google.protobuf.Duration
145, // 48: specs.EtcdBackupSettings.max_interval:type_name -> google.protobuf.Duration
132, // 49: specs.MachineClassSpec.auto_provision:type_name -> specs.MachineClassSpec.Provision
133, // 50: specs.MachineConfigGenOptionsSpec.install_image:type_name -> specs.MachineConfigGenOptionsSpec.InstallImage
134, // 51: specs.KubernetesUsageSpec.cpu:type_name -> specs.KubernetesUsageSpec.Quantity
@ -9822,38 +9835,39 @@ var file_omni_specs_omni_proto_depIdxs = []int32{
137, // 56: specs.TalosExtensionsSpec.items:type_name -> specs.TalosExtensionsSpec.Info
17, // 57: specs.ExtensionsConfigurationStatusSpec.phase:type_name -> specs.ExtensionsConfigurationStatusSpec.Phase
138, // 58: specs.MachineExtensionsStatusSpec.extensions:type_name -> specs.MachineExtensionsStatusSpec.Item
139, // 59: specs.ClusterStatusMetricsSpec.phases:type_name -> specs.ClusterStatusMetricsSpec.PhasesEntry
25, // 60: specs.MachineRequestSetSpec.meta_values:type_name -> specs.MetaValue
3, // 61: specs.MachineRequestSetSpec.grpc_tunnel:type_name -> specs.GrpcTunnelMode
140, // 62: specs.ClusterDiagnosticsSpec.nodes:type_name -> specs.ClusterDiagnosticsSpec.Node
19, // 63: specs.ClusterMachineRequestStatusSpec.stage:type_name -> specs.ClusterMachineRequestStatusSpec.Stage
21, // 64: specs.InfraMachineConfigSpec.power_state:type_name -> specs.InfraMachineConfigSpec.MachinePowerState
20, // 65: specs.InfraMachineConfigSpec.acceptance_status:type_name -> specs.InfraMachineConfigSpec.AcceptanceStatus
141, // 66: specs.InfraMachineBMCConfigSpec.ipmi:type_name -> specs.InfraMachineBMCConfigSpec.IPMI
142, // 67: specs.InfraMachineBMCConfigSpec.api:type_name -> specs.InfraMachineBMCConfigSpec.API
143, // 68: specs.InfraProviderCombinedStatusSpec.health:type_name -> specs.InfraProviderCombinedStatusSpec.Health
117, // 69: specs.MachineStatusSpec.HardwareStatus.processors:type_name -> specs.MachineStatusSpec.HardwareStatus.Processor
118, // 70: specs.MachineStatusSpec.HardwareStatus.memory_modules:type_name -> specs.MachineStatusSpec.HardwareStatus.MemoryModule
119, // 71: specs.MachineStatusSpec.HardwareStatus.blockdevices:type_name -> specs.MachineStatusSpec.HardwareStatus.BlockDevice
120, // 72: specs.MachineStatusSpec.NetworkStatus.network_links:type_name -> specs.MachineStatusSpec.NetworkStatus.NetworkLinkStatus
24, // 73: specs.MachineStatusSpec.Schematic.overlay:type_name -> specs.Overlay
25, // 74: specs.MachineStatusSpec.Schematic.meta_values:type_name -> specs.MetaValue
10, // 75: specs.MachineSetSpec.MachineClass.allocation_type:type_name -> specs.MachineSetSpec.MachineClass.Type
11, // 76: specs.MachineSetSpec.MachineAllocation.allocation_type:type_name -> specs.MachineSetSpec.MachineAllocation.Type
126, // 77: specs.MachineSetSpec.UpdateStrategyConfig.rolling:type_name -> specs.MachineSetSpec.RollingUpdateStrategyConfig
2, // 78: specs.ControlPlaneStatusSpec.Condition.type:type_name -> specs.ConditionType
14, // 79: specs.ControlPlaneStatusSpec.Condition.status:type_name -> specs.ControlPlaneStatusSpec.Condition.Status
15, // 80: specs.ControlPlaneStatusSpec.Condition.severity:type_name -> specs.ControlPlaneStatusSpec.Condition.Severity
130, // 81: specs.KubernetesStatusSpec.NodeStaticPods.static_pods:type_name -> specs.KubernetesStatusSpec.StaticPodStatus
25, // 82: specs.MachineClassSpec.Provision.meta_values:type_name -> specs.MetaValue
3, // 83: specs.MachineClassSpec.Provision.grpc_tunnel:type_name -> specs.GrpcTunnelMode
23, // 84: specs.MachineConfigGenOptionsSpec.InstallImage.security_state:type_name -> specs.SecurityState
18, // 85: specs.MachineExtensionsStatusSpec.Item.phase:type_name -> specs.MachineExtensionsStatusSpec.Item.Phase
86, // [86:86] is the sub-list for method output_type
86, // [86:86] is the sub-list for method input_type
86, // [86:86] is the sub-list for extension type_name
86, // [86:86] is the sub-list for extension extendee
0, // [0:86] is the sub-list for field type_name
139, // 59: specs.MachineStatusMetricsSpec.versions_map:type_name -> specs.MachineStatusMetricsSpec.VersionsMapEntry
140, // 60: specs.ClusterStatusMetricsSpec.phases:type_name -> specs.ClusterStatusMetricsSpec.PhasesEntry
25, // 61: specs.MachineRequestSetSpec.meta_values:type_name -> specs.MetaValue
3, // 62: specs.MachineRequestSetSpec.grpc_tunnel:type_name -> specs.GrpcTunnelMode
141, // 63: specs.ClusterDiagnosticsSpec.nodes:type_name -> specs.ClusterDiagnosticsSpec.Node
19, // 64: specs.ClusterMachineRequestStatusSpec.stage:type_name -> specs.ClusterMachineRequestStatusSpec.Stage
21, // 65: specs.InfraMachineConfigSpec.power_state:type_name -> specs.InfraMachineConfigSpec.MachinePowerState
20, // 66: specs.InfraMachineConfigSpec.acceptance_status:type_name -> specs.InfraMachineConfigSpec.AcceptanceStatus
142, // 67: specs.InfraMachineBMCConfigSpec.ipmi:type_name -> specs.InfraMachineBMCConfigSpec.IPMI
143, // 68: specs.InfraMachineBMCConfigSpec.api:type_name -> specs.InfraMachineBMCConfigSpec.API
144, // 69: specs.InfraProviderCombinedStatusSpec.health:type_name -> specs.InfraProviderCombinedStatusSpec.Health
117, // 70: specs.MachineStatusSpec.HardwareStatus.processors:type_name -> specs.MachineStatusSpec.HardwareStatus.Processor
118, // 71: specs.MachineStatusSpec.HardwareStatus.memory_modules:type_name -> specs.MachineStatusSpec.HardwareStatus.MemoryModule
119, // 72: specs.MachineStatusSpec.HardwareStatus.blockdevices:type_name -> specs.MachineStatusSpec.HardwareStatus.BlockDevice
120, // 73: specs.MachineStatusSpec.NetworkStatus.network_links:type_name -> specs.MachineStatusSpec.NetworkStatus.NetworkLinkStatus
24, // 74: specs.MachineStatusSpec.Schematic.overlay:type_name -> specs.Overlay
25, // 75: specs.MachineStatusSpec.Schematic.meta_values:type_name -> specs.MetaValue
10, // 76: specs.MachineSetSpec.MachineClass.allocation_type:type_name -> specs.MachineSetSpec.MachineClass.Type
11, // 77: specs.MachineSetSpec.MachineAllocation.allocation_type:type_name -> specs.MachineSetSpec.MachineAllocation.Type
126, // 78: specs.MachineSetSpec.UpdateStrategyConfig.rolling:type_name -> specs.MachineSetSpec.RollingUpdateStrategyConfig
2, // 79: specs.ControlPlaneStatusSpec.Condition.type:type_name -> specs.ConditionType
14, // 80: specs.ControlPlaneStatusSpec.Condition.status:type_name -> specs.ControlPlaneStatusSpec.Condition.Status
15, // 81: specs.ControlPlaneStatusSpec.Condition.severity:type_name -> specs.ControlPlaneStatusSpec.Condition.Severity
130, // 82: specs.KubernetesStatusSpec.NodeStaticPods.static_pods:type_name -> specs.KubernetesStatusSpec.StaticPodStatus
25, // 83: specs.MachineClassSpec.Provision.meta_values:type_name -> specs.MetaValue
3, // 84: specs.MachineClassSpec.Provision.grpc_tunnel:type_name -> specs.GrpcTunnelMode
23, // 85: specs.MachineConfigGenOptionsSpec.InstallImage.security_state:type_name -> specs.SecurityState
18, // 86: specs.MachineExtensionsStatusSpec.Item.phase:type_name -> specs.MachineExtensionsStatusSpec.Item.Phase
87, // [87:87] is the sub-list for method output_type
87, // [87:87] is the sub-list for method input_type
87, // [87:87] is the sub-list for extension type_name
87, // [87:87] is the sub-list for extension extendee
0, // [0:87] is the sub-list for field type_name
}
func init() { file_omni_specs_omni_proto_init() }
@ -9872,7 +9886,7 @@ func file_omni_specs_omni_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_omni_specs_omni_proto_rawDesc), len(file_omni_specs_omni_proto_rawDesc)),
NumEnums: 22,
NumMessages: 122,
NumMessages: 123,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -1225,6 +1225,8 @@ message MachineStatusMetricsSpec {
uint32 connected_machines_count = 2;
uint32 allocated_machines_count = 3;
uint32 pending_machines_count = 4;
map<string, int32> versions_map = 5;
}
// ClusterStatusMetricsSpec provides aggregated state of the number of clusters in not ready phase.

View File

@ -2212,6 +2212,13 @@ func (m *MachineStatusMetricsSpec) CloneVT() *MachineStatusMetricsSpec {
r.ConnectedMachinesCount = m.ConnectedMachinesCount
r.AllocatedMachinesCount = m.AllocatedMachinesCount
r.PendingMachinesCount = m.PendingMachinesCount
if rhs := m.VersionsMap; rhs != nil {
tmpContainer := make(map[string]int32, len(rhs))
for k, v := range rhs {
tmpContainer[k] = v
}
r.VersionsMap = tmpContainer
}
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
@ -5618,6 +5625,18 @@ func (this *MachineStatusMetricsSpec) EqualVT(that *MachineStatusMetricsSpec) bo
if this.PendingMachinesCount != that.PendingMachinesCount {
return false
}
if len(this.VersionsMap) != len(that.VersionsMap) {
return false
}
for i, vx := range this.VersionsMap {
vy, ok := that.VersionsMap[i]
if !ok {
return false
}
if vx != vy {
return false
}
}
return string(this.unknownFields) == string(that.unknownFields)
}
@ -12103,6 +12122,23 @@ func (m *MachineStatusMetricsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, err
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.VersionsMap) > 0 {
for k := range m.VersionsMap {
v := m.VersionsMap[k]
baseI := i
i = protohelpers.EncodeVarint(dAtA, i, uint64(v))
i--
dAtA[i] = 0x10
i -= len(k)
copy(dAtA[i:], k)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k)))
i--
dAtA[i] = 0xa
i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i))
i--
dAtA[i] = 0x2a
}
}
if m.PendingMachinesCount != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PendingMachinesCount))
i--
@ -15429,6 +15465,14 @@ func (m *MachineStatusMetricsSpec) SizeVT() (n int) {
if m.PendingMachinesCount != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.PendingMachinesCount))
}
if len(m.VersionsMap) > 0 {
for k, v := range m.VersionsMap {
_ = k
_ = v
mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + protohelpers.SizeOfVarint(uint64(v))
n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize))
}
}
n += len(m.unknownFields)
return n
}
@ -30740,6 +30784,119 @@ func (m *MachineStatusMetricsSpec) UnmarshalVT(dAtA []byte) error {
break
}
}
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field VersionsMap", 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.VersionsMap == nil {
m.VersionsMap = make(map[string]int32)
}
var mapkey string
var mapvalue int32
for iNdEx < postIndex {
entryPreIndex := 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)
if fieldNum == 1 {
var stringLenmapkey uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLenmapkey |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLenmapkey := int(stringLenmapkey)
if intStringLenmapkey < 0 {
return protohelpers.ErrInvalidLength
}
postStringIndexmapkey := iNdEx + intStringLenmapkey
if postStringIndexmapkey < 0 {
return protohelpers.ErrInvalidLength
}
if postStringIndexmapkey > l {
return io.ErrUnexpectedEOF
}
mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
iNdEx = postStringIndexmapkey
} else if fieldNum == 2 {
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
mapvalue |= int32(b&0x7F) << shift
if b < 0x80 {
break
}
}
} else {
iNdEx = entryPreIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > postIndex {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
m.VersionsMap[mapkey] = mapvalue
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])

View File

@ -10,7 +10,9 @@ import (
"github.com/blang/semver"
)
var minSupportedSecureTokensVersion = semver.MustParse("1.6.0")
// MinSupportedSecureTokensVersion is 1.6.0.
// Below 1.6.0 Talos doesn't properly report node unique tokens to Omni.
var MinSupportedSecureTokensVersion = semver.MustParse("1.6.0")
// SupportsSecureJoinTokens checks if the Talos version supports secure join tokens.
func SupportsSecureJoinTokens(talosVersion string) bool {
@ -19,5 +21,5 @@ func SupportsSecureJoinTokens(talosVersion string) bool {
return false
}
return v.GTE(minSupportedSecureTokensVersion)
return v.GTE(MinSupportedSecureTokensVersion)
}

View File

@ -93,6 +93,10 @@ var rootCmd = &cobra.Command{
}
}()
if err = config.ValidateState(ctx, state.Default()); err != nil {
return err
}
if constants.IsDebugBuild {
logger.Warn("running debug build")
}

View File

@ -131,14 +131,15 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// ) to obtain a formatter capable of generating timestamps in this format.
//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
// Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must
// be between -315576000000 and 315576000000 inclusive (which corresponds to
// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).
int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// Non-negative fractions of a second at nanosecond resolution. This field is
// the nanosecond portion of the duration, not an alternative to seconds.
// Negative second values with fractions must still have non-negative nanos
// values that count forward in time. Must be between 0 and 999,999,999
// inclusive.
int32 nanos = 2;
}

View File

@ -807,6 +807,7 @@ export type MachineStatusMetricsSpec = {
connected_machines_count?: number
allocated_machines_count?: number
pending_machines_count?: number
versions_map?: {[key: string]: number}
}
export type ClusterStatusMetricsSpec = {

View File

@ -27,7 +27,7 @@ import (
//nolint:govet
type MachineStatusMetricsController struct {
versionsMu sync.Mutex
versionsMap map[string]int
versionsMap map[string]int32
metricsOnce sync.Once
metricNumMachines prometheus.Gauge
@ -120,7 +120,7 @@ func (ctrl *MachineStatusMetricsController) Run(ctx context.Context, r controlle
var machines, connectedMachines, allocatedMachines int
ctrl.versionsMu.Lock()
ctrl.versionsMap = map[string]int{}
ctrl.versionsMap = map[string]int32{}
for ms := range list.All() {
machines++
@ -149,6 +149,7 @@ func (ctrl *MachineStatusMetricsController) Run(ctx context.Context, r controlle
res.TypedSpec().Value.RegisteredMachinesCount = uint32(machines)
res.TypedSpec().Value.AllocatedMachinesCount = uint32(allocatedMachines)
res.TypedSpec().Value.PendingMachinesCount = uint32(pendingMachines)
res.TypedSpec().Value.VersionsMap = ctrl.versionsMap
return nil
},

View File

@ -8,6 +8,7 @@ package config
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -16,6 +17,7 @@ import (
"os"
"time"
"github.com/cosi-project/runtime/pkg/state"
"github.com/go-playground/validator/v10"
"github.com/siderolabs/gen/xyaml"
"github.com/siderolabs/talos/pkg/machinery/config/merge"
@ -27,6 +29,7 @@ import (
consts "github.com/siderolabs/omni/client/pkg/constants"
"github.com/siderolabs/omni/client/pkg/omni/resources/common"
"github.com/siderolabs/omni/internal/pkg/auth/role"
"github.com/siderolabs/omni/internal/pkg/config/validations"
)
const (
@ -271,6 +274,18 @@ type Params struct {
Features Features `yaml:"features"`
}
// ValidateState validate Omni params against the current state of Omni instance.
// Add any hooks that would need to validate the config against the state here.
func (p *Params) ValidateState(ctx context.Context, st state.State) error {
if p.Services.Siderolink.JoinTokensMode == JoinTokensModeStrict {
if err := validations.EnsureAllMachinesSupportStrictTokens(ctx, st); err != nil {
return err
}
}
return nil
}
// Validate Omni params.
func (p *Params) Validate() error {
validate := validator.New(validator.WithRequiredStructEnabled())

View File

@ -7,12 +7,21 @@
package config_test
import (
"context"
_ "embed"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"github.com/siderolabs/omni/client/pkg/omni/resources"
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
"github.com/siderolabs/omni/internal/pkg/config"
)
@ -61,6 +70,39 @@ func TestMergeConfig(t *testing.T) {
require.True(t, cfg.Services.EmbeddedDiscoveryService.Enabled)
}
func TestValidateStateConfig(t *testing.T) {
state := state.WrapCore(namespaced.NewState(inmem.Build))
cfg, err := config.FromBytes(configFull)
require.NoError(t, err)
ctx, cancel := context.WithTimeout(t.Context(), time.Second*5)
defer cancel()
// no machines
assert.NoError(t, cfg.ValidateState(ctx, state))
// fail with machines below 1.6, pass with above 1.6
machine := omni.NewMachineStatus(resources.DefaultNamespace, "1")
machine.TypedSpec().Value.TalosVersion = "v1.5.5"
require.NoError(t, state.Create(ctx, machine))
assert.Error(t, cfg.ValidateState(ctx, state))
_, err = safe.StateUpdateWithConflicts(ctx, state, machine.Metadata(), func(res *omni.MachineStatus) error {
res.TypedSpec().Value.TalosVersion = "v1.6.0"
return nil
})
require.NoError(t, err)
assert.NoError(t, cfg.ValidateState(ctx, state))
}
func TestValidateConfig(t *testing.T) {
for _, tt := range []struct {
name string

View File

@ -0,0 +1,73 @@
// Copyright (c) 2025 Sidero Labs, Inc.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
package validations
import (
"context"
"fmt"
"strings"
"github.com/blang/semver"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/gertd/go-pluralize"
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
"github.com/siderolabs/omni/client/pkg/siderolink"
)
// EnsureAllMachinesSupportStrictTokens makes sure that Omni state doesn't have any machines running Talos
// below 1.6 and strict tokens mode is enabled.
func EnsureAllMachinesSupportStrictTokens(ctx context.Context, st state.State) error {
count, err := getMachinesBelowTalosVersion(ctx, st, siderolink.MinSupportedSecureTokensVersion)
if err != nil {
return err
}
if count == 0 {
return nil
}
return fmt.Errorf("detected %s running Talos version below 1.6. 'strict' join token is not supported on the instance\n"+
"Please upgrade the machines\n"+
"Or change '--join-tokens-mode' flag to 'legacyAllowed'", pluralize.NewClient().Pluralize("machine", int(count), true))
}
func getMachinesBelowTalosVersion(ctx context.Context, st state.State, belowVersion semver.Version) (int32, error) {
statuses, err := safe.ReaderListAll[*omni.MachineStatus](ctx, st)
if err != nil {
return 0, err
}
versions := map[string]int32{}
for status := range statuses.All() {
if status.TypedSpec().Value.TalosVersion == "" {
continue
}
versions[status.TypedSpec().Value.TalosVersion]++
}
if len(versions) == 0 {
return 0, nil
}
var count int32
for version, c := range versions {
v, err := semver.ParseTolerant(strings.TrimLeft(version, "v"))
if err != nil {
continue
}
if v.LT(belowVersion) {
count += c
}
}
return count, nil
}

View File

@ -0,0 +1,7 @@
// Copyright (c) 2025 Sidero Labs, Inc.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
// Package validations contains config validations which run against the existing Omni state.
package validations