diff --git a/client/api/omni/specs/omni.pb.go b/client/api/omni/specs/omni.pb.go index ef93de6a..0c84bb17 100644 --- a/client/api/omni/specs/omni.pb.go +++ b/client/api/omni/specs/omni.pb.go @@ -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, }, diff --git a/client/api/omni/specs/omni.proto b/client/api/omni/specs/omni.proto index 1aa3c524..75c2f2f8 100644 --- a/client/api/omni/specs/omni.proto +++ b/client/api/omni/specs/omni.proto @@ -1225,6 +1225,8 @@ message MachineStatusMetricsSpec { uint32 connected_machines_count = 2; uint32 allocated_machines_count = 3; uint32 pending_machines_count = 4; + + map versions_map = 5; } // ClusterStatusMetricsSpec provides aggregated state of the number of clusters in not ready phase. diff --git a/client/api/omni/specs/omni_vtproto.pb.go b/client/api/omni/specs/omni_vtproto.pb.go index 4ec800e0..cd2b957a 100644 --- a/client/api/omni/specs/omni_vtproto.pb.go +++ b/client/api/omni/specs/omni_vtproto.pb.go @@ -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:]) diff --git a/client/pkg/siderolink/secure_tokens.go b/client/pkg/siderolink/secure_tokens.go index 331dd34a..e7508303 100644 --- a/client/pkg/siderolink/secure_tokens.go +++ b/client/pkg/siderolink/secure_tokens.go @@ -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) } diff --git a/cmd/omni/cmd/cmd.go b/cmd/omni/cmd/cmd.go index 60a1beca..514ce10e 100644 --- a/cmd/omni/cmd/cmd.go +++ b/cmd/omni/cmd/cmd.go @@ -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") } diff --git a/frontend/src/api/google/protobuf/timestamp.proto b/frontend/src/api/google/protobuf/timestamp.proto index fd0bc07d..fd308bd4 100644 --- a/frontend/src/api/google/protobuf/timestamp.proto +++ b/frontend/src/api/google/protobuf/timestamp.proto @@ -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; } diff --git a/frontend/src/api/omni/specs/omni.pb.ts b/frontend/src/api/omni/specs/omni.pb.ts index 204fbef2..d0e5af11 100644 --- a/frontend/src/api/omni/specs/omni.pb.ts +++ b/frontend/src/api/omni/specs/omni.pb.ts @@ -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 = { diff --git a/internal/backend/runtime/omni/controllers/omni/machine_status_metrics.go b/internal/backend/runtime/omni/controllers/omni/machine_status_metrics.go index 94a94e8f..2e41020e 100644 --- a/internal/backend/runtime/omni/controllers/omni/machine_status_metrics.go +++ b/internal/backend/runtime/omni/controllers/omni/machine_status_metrics.go @@ -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 }, diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index f6414f4a..d7fcb5c0 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -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()) diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index f526363c..10be63ec 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -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 diff --git a/internal/pkg/config/validations/join_token_strict.go b/internal/pkg/config/validations/join_token_strict.go new file mode 100644 index 00000000..8724dd56 --- /dev/null +++ b/internal/pkg/config/validations/join_token_strict.go @@ -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 +} diff --git a/internal/pkg/config/validations/validations.go b/internal/pkg/config/validations/validations.go new file mode 100644 index 00000000..5d48fc56 --- /dev/null +++ b/internal/pkg/config/validations/validations.go @@ -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