From e4bb4165e9d25cceabd2e4cd73e5a53b4d5756ef Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 14 Apr 2022 17:39:47 +0300 Subject: [PATCH] feat: restructure HW information in `Server` resources (v1alpha2) This is basically subset of PR #735 with only CRD changes without actual code changes to use new CRDs. As storage version is v1alpha2, every access goes twice via conversion webhooks (for better test coverage). Other parts of #735 will be incorporated in a follow-up PR, I decided to split things up for easier review. Example: ```bash $ kubectl get servers.v1alpha1.metal.sidero.dev 49fd7c2d-1ba4-4157-8cc0-3f7212f119f0 -o yaml ... cpu: manufacturer: QEMU version: pc-q35-6.0 hostname: pxe-3 managementApi: endpoint: 172.25.0.1:39565 system: family: Unknown manufacturer: QEMU productName: Standard PC (Q35 + ICH9, 2009) serialNumber: Unknown skuNumber: Unknown version: pc-q35-6.0 $ kubectl get servers.v1alpha2.metal.sidero.dev 49fd7c2d-1ba4-4157-8cc0-3f7212f119f0 -o yaml ... hardware: compute: processors: - manufacturer: QEMU productName: pc-q35-6.0 system: family: Unknown manufacturer: QEMU productName: Standard PC (Q35 + ICH9, 2009) serialNumber: Unknown skuNumber: Unknown version: pc-q35-6.0 ``` We can make more changes to v1alpha2 resources in follow-up PRs. Co-authored-by: Gerard de Leeuw Signed-off-by: Gerard de Leeuw Signed-off-by: Andrey Smirnov --- Dockerfile | 1 + .../crd/patches/webhook_in_metalclusters.yaml | 2 +- .../crd/patches/webhook_in_metalmachines.yaml | 2 +- .../webhook_in_metalmachinetemplates.yaml | 2 +- .../patches/webhook_in_serverbindings.yaml | 2 +- app/sidero-controller-manager/PROJECT | 9 + .../api/v1alpha1/doc.go | 7 + .../api/v1alpha1/environment_conversion.go | 56 ++ .../api/v1alpha1/groupversion_info.go | 3 + .../api/v1alpha1/server_conversion.go | 133 +++ .../api/v1alpha1/server_conversion_test.go | 99 ++ .../api/v1alpha1/serverclass_conversion.go | 126 +++ .../api/v1alpha1/zz_generated.conversion.go | 886 ++++++++++++++++++ .../api/v1alpha2/doc.go | 5 + .../api/v1alpha2/environment_conversion.go | 8 + .../api/v1alpha2/environment_types.go | 123 +++ .../api/v1alpha2/environment_webhook.go | 21 + .../api/v1alpha2/groupversion_info.go | 24 + .../api/v1alpha2/server_conversion.go | 8 + .../api/v1alpha2/server_types.go | 329 +++++++ .../api/v1alpha2/server_types_test.go | 147 +++ .../api/v1alpha2/server_webhook.go | 118 +++ .../api/v1alpha2/serverclass_conversion.go | 8 + .../api/v1alpha2/serverclass_filter.go | 110 +++ .../api/v1alpha2/serverclass_filter_test.go | 378 ++++++++ .../api/v1alpha2/serverclass_types.go | 84 ++ .../api/v1alpha2/serverclass_webhook.go | 21 + .../api/v1alpha2/types.go | 16 + .../api/v1alpha2/zz_generated.deepcopy.go | 771 +++++++++++++++ .../bases/metal.sidero.dev_environments.yaml | 81 ++ .../bases/metal.sidero.dev_serverclasses.yaml | 315 +++++++ .../crd/bases/metal.sidero.dev_servers.yaml | 445 +++++++++ .../crd/patches/webhook_in_environments.yaml | 2 +- .../crd/patches/webhook_in_serverclasses.yaml | 2 +- .../crd/patches/webhook_in_servers.yaml | 2 +- .../config/webhook/manifests.yaml | 21 + app/sidero-controller-manager/main.go | 74 +- sfyra/pkg/tests/compatibility.go | 4 +- 38 files changed, 4418 insertions(+), 27 deletions(-) create mode 100644 app/sidero-controller-manager/api/v1alpha1/doc.go create mode 100644 app/sidero-controller-manager/api/v1alpha1/environment_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha1/server_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha1/server_conversion_test.go create mode 100644 app/sidero-controller-manager/api/v1alpha1/serverclass_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha1/zz_generated.conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/doc.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/environment_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/environment_types.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/environment_webhook.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/groupversion_info.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/server_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/server_types.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/server_types_test.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/server_webhook.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/serverclass_conversion.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/serverclass_filter.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/serverclass_filter_test.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/serverclass_types.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/serverclass_webhook.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/types.go create mode 100644 app/sidero-controller-manager/api/v1alpha2/zz_generated.deepcopy.go diff --git a/Dockerfile b/Dockerfile index 1acad1232..94c30c173 100644 --- a/Dockerfile +++ b/Dockerfile @@ -79,6 +79,7 @@ RUN protoc -I/src/app/sidero-controller-manager/internal/api \ api.proto RUN --mount=type=cache,target=/.cache controller-gen object:headerFile="./hack/boilerplate.go.txt" paths="./..." RUN --mount=type=cache,target=/.cache conversion-gen --input-dirs="./app/caps-controller-manager/api/v1alpha2" --output-base ./ --output-file-base="zz_generated.conversion" --go-header-file="./hack/boilerplate.go.txt" +RUN --mount=type=cache,target=/.cache conversion-gen --input-dirs="./app/sidero-controller-manager/api/v1alpha1" --output-base ./ --output-file-base="zz_generated.conversion" --go-header-file="./hack/boilerplate.go.txt" ARG MODULE RUN --mount=type=cache,target=/.cache gofumports -w -local ${MODULE} . diff --git a/app/caps-controller-manager/config/crd/patches/webhook_in_metalclusters.yaml b/app/caps-controller-manager/config/crd/patches/webhook_in_metalclusters.yaml index 2324040ba..e85ab4769 100644 --- a/app/caps-controller-manager/config/crd/patches/webhook_in_metalclusters.yaml +++ b/app/caps-controller-manager/config/crd/patches/webhook_in_metalclusters.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: caps-webhook-service path: /convert diff --git a/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachines.yaml b/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachines.yaml index 71510e9dd..2862905f8 100644 --- a/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachines.yaml +++ b/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachines.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: caps-webhook-service path: /convert diff --git a/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachinetemplates.yaml b/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachinetemplates.yaml index 600a8964b..6ddb39c7b 100644 --- a/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachinetemplates.yaml +++ b/app/caps-controller-manager/config/crd/patches/webhook_in_metalmachinetemplates.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: caps-webhook-service path: /convert diff --git a/app/caps-controller-manager/config/crd/patches/webhook_in_serverbindings.yaml b/app/caps-controller-manager/config/crd/patches/webhook_in_serverbindings.yaml index 4ffa25713..b4269944d 100644 --- a/app/caps-controller-manager/config/crd/patches/webhook_in_serverbindings.yaml +++ b/app/caps-controller-manager/config/crd/patches/webhook_in_serverbindings.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: caps-webhook-service path: /convert diff --git a/app/sidero-controller-manager/PROJECT b/app/sidero-controller-manager/PROJECT index 6bf695d0a..1472e279c 100644 --- a/app/sidero-controller-manager/PROJECT +++ b/app/sidero-controller-manager/PROJECT @@ -10,4 +10,13 @@ resources: - group: metal kind: ServerClass version: v1alpha1 +- group: metal + kind: Environment + version: v1alpha2 +- group: metal + kind: Server + version: v1alpha2 +- group: metal + kind: ServerClass + version: v1alpha2 version: "2" diff --git a/app/sidero-controller-manager/api/v1alpha1/doc.go b/app/sidero-controller-manager/api/v1alpha1/doc.go new file mode 100644 index 000000000..5aac563b1 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/doc.go @@ -0,0 +1,7 @@ +// 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 v1alpha1 + +// +k8s:conversion-gen=github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2 diff --git a/app/sidero-controller-manager/api/v1alpha1/environment_conversion.go b/app/sidero-controller-manager/api/v1alpha1/environment_conversion.go new file mode 100644 index 000000000..924bc0b2f --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/environment_conversion.go @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//nolint:golint,stylecheck +package v1alpha1 + +import ( + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + metalv1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +// ConvertTo converts this Environment to the Hub version (v1alpha2). +func (src *Environment) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.Environment) + if err := Convert_v1alpha1_Environment_To_v1alpha2_Environment(src, dst, nil); err != nil { + return err + } + + // Manually restore data from annotations + restored := &metalv1alpha2.Environment{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + return nil +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *Environment) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.Environment) + if err := Convert_v1alpha2_Environment_To_v1alpha1_Environment(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion. + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// ConvertTo converts this MetalMachineTemplateList to the Hub version (v1alpha3). +func (src *EnvironmentList) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.EnvironmentList) + return Convert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList(src, dst, nil) +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *EnvironmentList) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.EnvironmentList) + return Convert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList(src, dst, nil) +} diff --git a/app/sidero-controller-manager/api/v1alpha1/groupversion_info.go b/app/sidero-controller-manager/api/v1alpha1/groupversion_info.go index 201054aa4..44d835731 100644 --- a/app/sidero-controller-manager/api/v1alpha1/groupversion_info.go +++ b/app/sidero-controller-manager/api/v1alpha1/groupversion_info.go @@ -21,4 +21,7 @@ var ( // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme + + // localSchemeBuilder is used for type conversions. + localSchemeBuilder = SchemeBuilder.SchemeBuilder ) diff --git a/app/sidero-controller-manager/api/v1alpha1/server_conversion.go b/app/sidero-controller-manager/api/v1alpha1/server_conversion.go new file mode 100644 index 000000000..5b6d5d52b --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/server_conversion.go @@ -0,0 +1,133 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//nolint:golint,stylecheck +package v1alpha1 + +import ( + apiconversion "k8s.io/apimachinery/pkg/conversion" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + metalv1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +// ConvertTo converts this Server to the Hub version (v1alpha2). +func (src *Server) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.Server) + if err := Convert_v1alpha1_Server_To_v1alpha2_Server(src, dst, nil); err != nil { + return err + } + + // Manually restore data from annotations + restored := &metalv1alpha2.Server{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + return nil +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *Server) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.Server) + if err := Convert_v1alpha2_Server_To_v1alpha1_Server(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion. + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// ConvertTo converts this MetalMachineTemplateList to the Hub version (v1alpha3). +func (src *ServerList) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.ServerList) + return Convert_v1alpha1_ServerList_To_v1alpha2_ServerList(src, dst, nil) +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *ServerList) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.ServerList) + return Convert_v1alpha2_ServerList_To_v1alpha1_ServerList(src, dst, nil) +} + +// Convert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec converts to the Hub version (v1alpha2). +func Convert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec(in *ServerSpec, out *metalv1alpha2.ServerSpec, s apiconversion.Scope) error { + if err := autoConvert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec(in, out, s); err != nil { + return err + } + + // Manually convert SystemInformation to Hardware. + if in.SystemInformation != nil { + if out.Hardware == nil { + out.Hardware = &metalv1alpha2.HardwareInformation{} + } + + out.Hardware.System = &metalv1alpha2.SystemInformation{ + Manufacturer: in.SystemInformation.Manufacturer, + ProductName: in.SystemInformation.ProductName, + Version: in.SystemInformation.Version, + SerialNumber: in.SystemInformation.SerialNumber, + SKUNumber: in.SystemInformation.SKUNumber, + Family: in.SystemInformation.Family, + } + } + + // Manually convert CPU to Hardware. + if in.CPU != nil { + if out.Hardware == nil { + out.Hardware = &metalv1alpha2.HardwareInformation{} + } + + out.Hardware.Compute = &metalv1alpha2.ComputeInformation{ + Processors: []*metalv1alpha2.Processor{ + { + Manufacturer: in.CPU.Manufacturer, + ProductName: in.CPU.Version, + }, + }, + } + } + + return nil +} + +// Convert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec converts from the Hub version (v1alpha2). +func Convert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec(in *metalv1alpha2.ServerSpec, out *ServerSpec, s apiconversion.Scope) error { + if err := autoConvert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec(in, out, s); err != nil { + return err + } + + // Manually convert Hardware to SystemInformation. + if in.Hardware != nil && in.Hardware.System != nil { + out.SystemInformation = &SystemInformation{ + Manufacturer: in.Hardware.System.Manufacturer, + ProductName: in.Hardware.System.ProductName, + Version: in.Hardware.System.Version, + SerialNumber: in.Hardware.System.SerialNumber, + SKUNumber: in.Hardware.System.SKUNumber, + Family: in.Hardware.System.Family, + } + } + + // Manually convert Hardware to CPU. + if in.Hardware != nil && in.Hardware.Compute != nil && len(in.Hardware.Compute.Processors) > 0 { + cpu := in.Hardware.Compute.Processors[0] + + out.CPU = &CPUInformation{ + Manufacturer: cpu.Manufacturer, + Version: cpu.ProductName, + } + } + + return nil +} + +func Convert_v1alpha2_SystemInformation_To_v1alpha1_SystemInformation(in *metalv1alpha2.SystemInformation, out *SystemInformation, s apiconversion.Scope) error { + return autoConvert_v1alpha2_SystemInformation_To_v1alpha1_SystemInformation(in, out, s) +} diff --git a/app/sidero-controller-manager/api/v1alpha1/server_conversion_test.go b/app/sidero-controller-manager/api/v1alpha1/server_conversion_test.go new file mode 100644 index 000000000..93d8697f1 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/server_conversion_test.go @@ -0,0 +1,99 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package v1alpha1_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + metalv1alpha1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1" + metalv1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +func TestServerConvertV1alpha1V1Alpha2(t *testing.T) { + src := &metalv1alpha1.Server{ + Spec: metalv1alpha1.ServerSpec{ + Hostname: "example.com", + SystemInformation: &metalv1alpha1.SystemInformation{ + Manufacturer: "Sidero", + ProductName: "Server", + Version: "v1.0", + }, + CPU: &metalv1alpha1.CPUInformation{ + Manufacturer: "Sidero CPU", + Version: "v1", + }, + }, + } + dst := &metalv1alpha2.Server{} + + require.NoError(t, src.ConvertTo(dst)) + + assert.Equal(t, "example.com", dst.Spec.Hostname) + assert.Equal(t, + &metalv1alpha2.SystemInformation{ + Manufacturer: "Sidero", + ProductName: "Server", + Version: "v1.0", + }, + dst.Spec.Hardware.System, + ) + assert.Equal(t, + &metalv1alpha2.ComputeInformation{ + Processors: []*metalv1alpha2.Processor{ + { + Manufacturer: "Sidero CPU", + ProductName: "v1", + }, + }, + }, + dst.Spec.Hardware.Compute, + ) +} + +func TestServerConvertV1alpha2V1Alpha1(t *testing.T) { + src := &metalv1alpha2.Server{ + Spec: metalv1alpha2.ServerSpec{ + Hostname: "example.com", + Hardware: &metalv1alpha2.HardwareInformation{ + System: &metalv1alpha2.SystemInformation{ + Manufacturer: "Sidero", + ProductName: "Server", + Version: "v1.0", + }, + Compute: &metalv1alpha2.ComputeInformation{ + Processors: []*metalv1alpha2.Processor{ + { + Manufacturer: "Sidero CPU", + ProductName: "v1", + }, + }, + }, + }, + }, + } + dst := &metalv1alpha1.Server{} + + require.NoError(t, dst.ConvertFrom(src)) + + assert.Equal(t, "example.com", dst.Spec.Hostname) + assert.Equal(t, + &metalv1alpha1.SystemInformation{ + Manufacturer: "Sidero", + ProductName: "Server", + Version: "v1.0", + }, + dst.Spec.SystemInformation, + ) + assert.Equal(t, + &metalv1alpha1.CPUInformation{ + Manufacturer: "Sidero CPU", + Version: "v1", + }, + dst.Spec.CPU, + ) +} diff --git a/app/sidero-controller-manager/api/v1alpha1/serverclass_conversion.go b/app/sidero-controller-manager/api/v1alpha1/serverclass_conversion.go new file mode 100644 index 000000000..43d4e4017 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/serverclass_conversion.go @@ -0,0 +1,126 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//nolint:golint,stylecheck +package v1alpha1 + +import ( + apiconversion "k8s.io/apimachinery/pkg/conversion" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + metalv1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +// ConvertTo converts this ServerClass to the Hub version (v1alpha2). +func (src *ServerClass) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.ServerClass) + if err := Convert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(src, dst, nil); err != nil { + return err + } + + // Manually restore data from annotations + restored := &metalv1alpha2.ServerClass{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + return nil +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *ServerClass) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.ServerClass) + if err := Convert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion. + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// ConvertTo converts this MetalMachineTemplateList to the Hub version (v1alpha3). +func (src *ServerClassList) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*metalv1alpha2.ServerClassList) + return Convert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList(src, dst, nil) +} + +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +func (dst *ServerClassList) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*metalv1alpha2.ServerClassList) + return Convert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList(src, dst, nil) +} + +// Convert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers converts to the Hub version (v1alpha2). +func Convert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers(in *Qualifiers, out *metalv1alpha2.Qualifiers, s apiconversion.Scope) error { + if err := autoConvert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers(in, out, s); err != nil { + return err + } + + // Manually convert SystemInformation to Hardware. + for _, v := range in.SystemInformation { + out.Hardware = append(out.Hardware, metalv1alpha2.HardwareInformation{ + System: &metalv1alpha2.SystemInformation{ + Manufacturer: v.Manufacturer, + ProductName: v.ProductName, + Version: v.Version, + SerialNumber: v.SerialNumber, + SKUNumber: v.SKUNumber, + Family: v.Family, + }, + }) + } + + // Manually convert CPU to Hardware. + for _, v := range in.CPU { + out.Hardware = append(out.Hardware, metalv1alpha2.HardwareInformation{ + Compute: &metalv1alpha2.ComputeInformation{ + Processors: []*metalv1alpha2.Processor{ + { + Manufacturer: v.Manufacturer, + ProductName: v.Version, + }, + }, + }, + }) + } + + return nil +} + +// Convert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers converts from the Hub version (v1alpha2). +func Convert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers(in *metalv1alpha2.Qualifiers, out *Qualifiers, s apiconversion.Scope) error { + if err := autoConvert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers(in, out, s); err != nil { + return err + } + + // Manually convert Hardware to SystemInformation or CPU. + for _, v := range in.Hardware { + if v.System != nil { + out.SystemInformation = append(out.SystemInformation, SystemInformation{ + Manufacturer: v.System.Manufacturer, + ProductName: v.System.ProductName, + Version: v.System.Version, + SerialNumber: v.System.SerialNumber, + SKUNumber: v.System.SKUNumber, + Family: v.System.Family, + }) + } + + if v.Compute != nil && len(v.Compute.Processors) > 0 { + cpu := v.Compute.Processors[0] + + out.CPU = append(out.CPU, CPUInformation{ + Manufacturer: cpu.Manufacturer, + Version: cpu.ProductName, + }) + } + } + + return nil +} diff --git a/app/sidero-controller-manager/api/v1alpha1/zz_generated.conversion.go b/app/sidero-controller-manager/api/v1alpha1/zz_generated.conversion.go new file mode 100644 index 000000000..ac273f357 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha1/zz_generated.conversion.go @@ -0,0 +1,886 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// 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/. + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + unsafe "unsafe" + + v1 "k8s.io/api/core/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" + + v1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" + types "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/types" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*Asset)(nil), (*v1alpha2.Asset)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Asset_To_v1alpha2_Asset(a.(*Asset), b.(*v1alpha2.Asset), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.Asset)(nil), (*Asset)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Asset_To_v1alpha1_Asset(a.(*v1alpha2.Asset), b.(*Asset), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*AssetCondition)(nil), (*v1alpha2.AssetCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_AssetCondition_To_v1alpha2_AssetCondition(a.(*AssetCondition), b.(*v1alpha2.AssetCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.AssetCondition)(nil), (*AssetCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_AssetCondition_To_v1alpha1_AssetCondition(a.(*v1alpha2.AssetCondition), b.(*AssetCondition), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BMC)(nil), (*v1alpha2.BMC)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_BMC_To_v1alpha2_BMC(a.(*BMC), b.(*v1alpha2.BMC), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.BMC)(nil), (*BMC)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_BMC_To_v1alpha1_BMC(a.(*v1alpha2.BMC), b.(*BMC), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ConfigPatches)(nil), (*v1alpha2.ConfigPatches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ConfigPatches_To_v1alpha2_ConfigPatches(a.(*ConfigPatches), b.(*v1alpha2.ConfigPatches), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ConfigPatches)(nil), (*ConfigPatches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ConfigPatches_To_v1alpha1_ConfigPatches(a.(*v1alpha2.ConfigPatches), b.(*ConfigPatches), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CredentialSource)(nil), (*v1alpha2.CredentialSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_CredentialSource_To_v1alpha2_CredentialSource(a.(*CredentialSource), b.(*v1alpha2.CredentialSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.CredentialSource)(nil), (*CredentialSource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_CredentialSource_To_v1alpha1_CredentialSource(a.(*v1alpha2.CredentialSource), b.(*CredentialSource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Environment)(nil), (*v1alpha2.Environment)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Environment_To_v1alpha2_Environment(a.(*Environment), b.(*v1alpha2.Environment), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.Environment)(nil), (*Environment)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Environment_To_v1alpha1_Environment(a.(*v1alpha2.Environment), b.(*Environment), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EnvironmentList)(nil), (*v1alpha2.EnvironmentList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList(a.(*EnvironmentList), b.(*v1alpha2.EnvironmentList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.EnvironmentList)(nil), (*EnvironmentList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList(a.(*v1alpha2.EnvironmentList), b.(*EnvironmentList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EnvironmentSpec)(nil), (*v1alpha2.EnvironmentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec(a.(*EnvironmentSpec), b.(*v1alpha2.EnvironmentSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.EnvironmentSpec)(nil), (*EnvironmentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec(a.(*v1alpha2.EnvironmentSpec), b.(*EnvironmentSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EnvironmentStatus)(nil), (*v1alpha2.EnvironmentStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus(a.(*EnvironmentStatus), b.(*v1alpha2.EnvironmentStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.EnvironmentStatus)(nil), (*EnvironmentStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus(a.(*v1alpha2.EnvironmentStatus), b.(*EnvironmentStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Initrd)(nil), (*v1alpha2.Initrd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Initrd_To_v1alpha2_Initrd(a.(*Initrd), b.(*v1alpha2.Initrd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.Initrd)(nil), (*Initrd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Initrd_To_v1alpha1_Initrd(a.(*v1alpha2.Initrd), b.(*Initrd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Kernel)(nil), (*v1alpha2.Kernel)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Kernel_To_v1alpha2_Kernel(a.(*Kernel), b.(*v1alpha2.Kernel), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.Kernel)(nil), (*Kernel)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Kernel_To_v1alpha1_Kernel(a.(*v1alpha2.Kernel), b.(*Kernel), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ManagementAPI)(nil), (*v1alpha2.ManagementAPI)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ManagementAPI_To_v1alpha2_ManagementAPI(a.(*ManagementAPI), b.(*v1alpha2.ManagementAPI), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ManagementAPI)(nil), (*ManagementAPI)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ManagementAPI_To_v1alpha1_ManagementAPI(a.(*v1alpha2.ManagementAPI), b.(*ManagementAPI), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*SecretKeyRef)(nil), (*v1alpha2.SecretKeyRef)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_SecretKeyRef_To_v1alpha2_SecretKeyRef(a.(*SecretKeyRef), b.(*v1alpha2.SecretKeyRef), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.SecretKeyRef)(nil), (*SecretKeyRef)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_SecretKeyRef_To_v1alpha1_SecretKeyRef(a.(*v1alpha2.SecretKeyRef), b.(*SecretKeyRef), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Server)(nil), (*v1alpha2.Server)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Server_To_v1alpha2_Server(a.(*Server), b.(*v1alpha2.Server), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.Server)(nil), (*Server)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Server_To_v1alpha1_Server(a.(*v1alpha2.Server), b.(*Server), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerClass)(nil), (*v1alpha2.ServerClass)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(a.(*ServerClass), b.(*v1alpha2.ServerClass), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerClass)(nil), (*ServerClass)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(a.(*v1alpha2.ServerClass), b.(*ServerClass), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerClassList)(nil), (*v1alpha2.ServerClassList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList(a.(*ServerClassList), b.(*v1alpha2.ServerClassList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerClassList)(nil), (*ServerClassList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList(a.(*v1alpha2.ServerClassList), b.(*ServerClassList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerClassSpec)(nil), (*v1alpha2.ServerClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec(a.(*ServerClassSpec), b.(*v1alpha2.ServerClassSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerClassSpec)(nil), (*ServerClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec(a.(*v1alpha2.ServerClassSpec), b.(*ServerClassSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerClassStatus)(nil), (*v1alpha2.ServerClassStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus(a.(*ServerClassStatus), b.(*v1alpha2.ServerClassStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerClassStatus)(nil), (*ServerClassStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus(a.(*v1alpha2.ServerClassStatus), b.(*ServerClassStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerList)(nil), (*v1alpha2.ServerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerList_To_v1alpha2_ServerList(a.(*ServerList), b.(*v1alpha2.ServerList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerList)(nil), (*ServerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerList_To_v1alpha1_ServerList(a.(*v1alpha2.ServerList), b.(*ServerList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ServerStatus)(nil), (*v1alpha2.ServerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus(a.(*ServerStatus), b.(*v1alpha2.ServerStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ServerStatus)(nil), (*ServerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus(a.(*v1alpha2.ServerStatus), b.(*ServerStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*SystemInformation)(nil), (*v1alpha2.SystemInformation)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_SystemInformation_To_v1alpha2_SystemInformation(a.(*SystemInformation), b.(*v1alpha2.SystemInformation), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*Qualifiers)(nil), (*v1alpha2.Qualifiers)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers(a.(*Qualifiers), b.(*v1alpha2.Qualifiers), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ServerSpec)(nil), (*v1alpha2.ServerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec(a.(*ServerSpec), b.(*v1alpha2.ServerSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.Qualifiers)(nil), (*Qualifiers)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers(a.(*v1alpha2.Qualifiers), b.(*Qualifiers), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ServerSpec)(nil), (*ServerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec(a.(*v1alpha2.ServerSpec), b.(*ServerSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.SystemInformation)(nil), (*SystemInformation)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_SystemInformation_To_v1alpha1_SystemInformation(a.(*v1alpha2.SystemInformation), b.(*SystemInformation), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_Asset_To_v1alpha2_Asset(in *Asset, out *v1alpha2.Asset, s conversion.Scope) error { + out.URL = in.URL + out.SHA512 = in.SHA512 + return nil +} + +// Convert_v1alpha1_Asset_To_v1alpha2_Asset is an autogenerated conversion function. +func Convert_v1alpha1_Asset_To_v1alpha2_Asset(in *Asset, out *v1alpha2.Asset, s conversion.Scope) error { + return autoConvert_v1alpha1_Asset_To_v1alpha2_Asset(in, out, s) +} + +func autoConvert_v1alpha2_Asset_To_v1alpha1_Asset(in *v1alpha2.Asset, out *Asset, s conversion.Scope) error { + out.URL = in.URL + out.SHA512 = in.SHA512 + return nil +} + +// Convert_v1alpha2_Asset_To_v1alpha1_Asset is an autogenerated conversion function. +func Convert_v1alpha2_Asset_To_v1alpha1_Asset(in *v1alpha2.Asset, out *Asset, s conversion.Scope) error { + return autoConvert_v1alpha2_Asset_To_v1alpha1_Asset(in, out, s) +} + +func autoConvert_v1alpha1_AssetCondition_To_v1alpha2_AssetCondition(in *AssetCondition, out *v1alpha2.AssetCondition, s conversion.Scope) error { + if err := Convert_v1alpha1_Asset_To_v1alpha2_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + out.Status = in.Status + out.Type = in.Type + return nil +} + +// Convert_v1alpha1_AssetCondition_To_v1alpha2_AssetCondition is an autogenerated conversion function. +func Convert_v1alpha1_AssetCondition_To_v1alpha2_AssetCondition(in *AssetCondition, out *v1alpha2.AssetCondition, s conversion.Scope) error { + return autoConvert_v1alpha1_AssetCondition_To_v1alpha2_AssetCondition(in, out, s) +} + +func autoConvert_v1alpha2_AssetCondition_To_v1alpha1_AssetCondition(in *v1alpha2.AssetCondition, out *AssetCondition, s conversion.Scope) error { + if err := Convert_v1alpha2_Asset_To_v1alpha1_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + out.Status = in.Status + out.Type = in.Type + return nil +} + +// Convert_v1alpha2_AssetCondition_To_v1alpha1_AssetCondition is an autogenerated conversion function. +func Convert_v1alpha2_AssetCondition_To_v1alpha1_AssetCondition(in *v1alpha2.AssetCondition, out *AssetCondition, s conversion.Scope) error { + return autoConvert_v1alpha2_AssetCondition_To_v1alpha1_AssetCondition(in, out, s) +} + +func autoConvert_v1alpha1_BMC_To_v1alpha2_BMC(in *BMC, out *v1alpha2.BMC, s conversion.Scope) error { + out.Endpoint = in.Endpoint + out.Port = in.Port + out.User = in.User + out.UserFrom = (*v1alpha2.CredentialSource)(unsafe.Pointer(in.UserFrom)) + out.Pass = in.Pass + out.PassFrom = (*v1alpha2.CredentialSource)(unsafe.Pointer(in.PassFrom)) + out.Interface = in.Interface + return nil +} + +// Convert_v1alpha1_BMC_To_v1alpha2_BMC is an autogenerated conversion function. +func Convert_v1alpha1_BMC_To_v1alpha2_BMC(in *BMC, out *v1alpha2.BMC, s conversion.Scope) error { + return autoConvert_v1alpha1_BMC_To_v1alpha2_BMC(in, out, s) +} + +func autoConvert_v1alpha2_BMC_To_v1alpha1_BMC(in *v1alpha2.BMC, out *BMC, s conversion.Scope) error { + out.Endpoint = in.Endpoint + out.Port = in.Port + out.User = in.User + out.UserFrom = (*CredentialSource)(unsafe.Pointer(in.UserFrom)) + out.Pass = in.Pass + out.PassFrom = (*CredentialSource)(unsafe.Pointer(in.PassFrom)) + out.Interface = in.Interface + return nil +} + +// Convert_v1alpha2_BMC_To_v1alpha1_BMC is an autogenerated conversion function. +func Convert_v1alpha2_BMC_To_v1alpha1_BMC(in *v1alpha2.BMC, out *BMC, s conversion.Scope) error { + return autoConvert_v1alpha2_BMC_To_v1alpha1_BMC(in, out, s) +} + +func autoConvert_v1alpha1_ConfigPatches_To_v1alpha2_ConfigPatches(in *ConfigPatches, out *v1alpha2.ConfigPatches, s conversion.Scope) error { + out.Op = in.Op + out.Path = in.Path + out.Value = in.Value + return nil +} + +// Convert_v1alpha1_ConfigPatches_To_v1alpha2_ConfigPatches is an autogenerated conversion function. +func Convert_v1alpha1_ConfigPatches_To_v1alpha2_ConfigPatches(in *ConfigPatches, out *v1alpha2.ConfigPatches, s conversion.Scope) error { + return autoConvert_v1alpha1_ConfigPatches_To_v1alpha2_ConfigPatches(in, out, s) +} + +func autoConvert_v1alpha2_ConfigPatches_To_v1alpha1_ConfigPatches(in *v1alpha2.ConfigPatches, out *ConfigPatches, s conversion.Scope) error { + out.Op = in.Op + out.Path = in.Path + out.Value = in.Value + return nil +} + +// Convert_v1alpha2_ConfigPatches_To_v1alpha1_ConfigPatches is an autogenerated conversion function. +func Convert_v1alpha2_ConfigPatches_To_v1alpha1_ConfigPatches(in *v1alpha2.ConfigPatches, out *ConfigPatches, s conversion.Scope) error { + return autoConvert_v1alpha2_ConfigPatches_To_v1alpha1_ConfigPatches(in, out, s) +} + +func autoConvert_v1alpha1_CredentialSource_To_v1alpha2_CredentialSource(in *CredentialSource, out *v1alpha2.CredentialSource, s conversion.Scope) error { + out.SecretKeyRef = (*v1alpha2.SecretKeyRef)(unsafe.Pointer(in.SecretKeyRef)) + return nil +} + +// Convert_v1alpha1_CredentialSource_To_v1alpha2_CredentialSource is an autogenerated conversion function. +func Convert_v1alpha1_CredentialSource_To_v1alpha2_CredentialSource(in *CredentialSource, out *v1alpha2.CredentialSource, s conversion.Scope) error { + return autoConvert_v1alpha1_CredentialSource_To_v1alpha2_CredentialSource(in, out, s) +} + +func autoConvert_v1alpha2_CredentialSource_To_v1alpha1_CredentialSource(in *v1alpha2.CredentialSource, out *CredentialSource, s conversion.Scope) error { + out.SecretKeyRef = (*SecretKeyRef)(unsafe.Pointer(in.SecretKeyRef)) + return nil +} + +// Convert_v1alpha2_CredentialSource_To_v1alpha1_CredentialSource is an autogenerated conversion function. +func Convert_v1alpha2_CredentialSource_To_v1alpha1_CredentialSource(in *v1alpha2.CredentialSource, out *CredentialSource, s conversion.Scope) error { + return autoConvert_v1alpha2_CredentialSource_To_v1alpha1_CredentialSource(in, out, s) +} + +func autoConvert_v1alpha1_Environment_To_v1alpha2_Environment(in *Environment, out *v1alpha2.Environment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_Environment_To_v1alpha2_Environment is an autogenerated conversion function. +func Convert_v1alpha1_Environment_To_v1alpha2_Environment(in *Environment, out *v1alpha2.Environment, s conversion.Scope) error { + return autoConvert_v1alpha1_Environment_To_v1alpha2_Environment(in, out, s) +} + +func autoConvert_v1alpha2_Environment_To_v1alpha1_Environment(in *v1alpha2.Environment, out *Environment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_Environment_To_v1alpha1_Environment is an autogenerated conversion function. +func Convert_v1alpha2_Environment_To_v1alpha1_Environment(in *v1alpha2.Environment, out *Environment, s conversion.Scope) error { + return autoConvert_v1alpha2_Environment_To_v1alpha1_Environment(in, out, s) +} + +func autoConvert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList(in *EnvironmentList, out *v1alpha2.EnvironmentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]v1alpha2.Environment)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList is an autogenerated conversion function. +func Convert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList(in *EnvironmentList, out *v1alpha2.EnvironmentList, s conversion.Scope) error { + return autoConvert_v1alpha1_EnvironmentList_To_v1alpha2_EnvironmentList(in, out, s) +} + +func autoConvert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList(in *v1alpha2.EnvironmentList, out *EnvironmentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]Environment)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList is an autogenerated conversion function. +func Convert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList(in *v1alpha2.EnvironmentList, out *EnvironmentList, s conversion.Scope) error { + return autoConvert_v1alpha2_EnvironmentList_To_v1alpha1_EnvironmentList(in, out, s) +} + +func autoConvert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec(in *EnvironmentSpec, out *v1alpha2.EnvironmentSpec, s conversion.Scope) error { + if err := Convert_v1alpha1_Kernel_To_v1alpha2_Kernel(&in.Kernel, &out.Kernel, s); err != nil { + return err + } + if err := Convert_v1alpha1_Initrd_To_v1alpha2_Initrd(&in.Initrd, &out.Initrd, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec is an autogenerated conversion function. +func Convert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec(in *EnvironmentSpec, out *v1alpha2.EnvironmentSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_EnvironmentSpec_To_v1alpha2_EnvironmentSpec(in, out, s) +} + +func autoConvert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec(in *v1alpha2.EnvironmentSpec, out *EnvironmentSpec, s conversion.Scope) error { + if err := Convert_v1alpha2_Kernel_To_v1alpha1_Kernel(&in.Kernel, &out.Kernel, s); err != nil { + return err + } + if err := Convert_v1alpha2_Initrd_To_v1alpha1_Initrd(&in.Initrd, &out.Initrd, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec is an autogenerated conversion function. +func Convert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec(in *v1alpha2.EnvironmentSpec, out *EnvironmentSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_EnvironmentSpec_To_v1alpha1_EnvironmentSpec(in, out, s) +} + +func autoConvert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus(in *EnvironmentStatus, out *v1alpha2.EnvironmentStatus, s conversion.Scope) error { + out.Conditions = *(*[]v1alpha2.AssetCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus is an autogenerated conversion function. +func Convert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus(in *EnvironmentStatus, out *v1alpha2.EnvironmentStatus, s conversion.Scope) error { + return autoConvert_v1alpha1_EnvironmentStatus_To_v1alpha2_EnvironmentStatus(in, out, s) +} + +func autoConvert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus(in *v1alpha2.EnvironmentStatus, out *EnvironmentStatus, s conversion.Scope) error { + out.Conditions = *(*[]AssetCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus is an autogenerated conversion function. +func Convert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus(in *v1alpha2.EnvironmentStatus, out *EnvironmentStatus, s conversion.Scope) error { + return autoConvert_v1alpha2_EnvironmentStatus_To_v1alpha1_EnvironmentStatus(in, out, s) +} + +func autoConvert_v1alpha1_Initrd_To_v1alpha2_Initrd(in *Initrd, out *v1alpha2.Initrd, s conversion.Scope) error { + if err := Convert_v1alpha1_Asset_To_v1alpha2_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_Initrd_To_v1alpha2_Initrd is an autogenerated conversion function. +func Convert_v1alpha1_Initrd_To_v1alpha2_Initrd(in *Initrd, out *v1alpha2.Initrd, s conversion.Scope) error { + return autoConvert_v1alpha1_Initrd_To_v1alpha2_Initrd(in, out, s) +} + +func autoConvert_v1alpha2_Initrd_To_v1alpha1_Initrd(in *v1alpha2.Initrd, out *Initrd, s conversion.Scope) error { + if err := Convert_v1alpha2_Asset_To_v1alpha1_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_Initrd_To_v1alpha1_Initrd is an autogenerated conversion function. +func Convert_v1alpha2_Initrd_To_v1alpha1_Initrd(in *v1alpha2.Initrd, out *Initrd, s conversion.Scope) error { + return autoConvert_v1alpha2_Initrd_To_v1alpha1_Initrd(in, out, s) +} + +func autoConvert_v1alpha1_Kernel_To_v1alpha2_Kernel(in *Kernel, out *v1alpha2.Kernel, s conversion.Scope) error { + if err := Convert_v1alpha1_Asset_To_v1alpha2_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + out.Args = *(*[]string)(unsafe.Pointer(&in.Args)) + return nil +} + +// Convert_v1alpha1_Kernel_To_v1alpha2_Kernel is an autogenerated conversion function. +func Convert_v1alpha1_Kernel_To_v1alpha2_Kernel(in *Kernel, out *v1alpha2.Kernel, s conversion.Scope) error { + return autoConvert_v1alpha1_Kernel_To_v1alpha2_Kernel(in, out, s) +} + +func autoConvert_v1alpha2_Kernel_To_v1alpha1_Kernel(in *v1alpha2.Kernel, out *Kernel, s conversion.Scope) error { + if err := Convert_v1alpha2_Asset_To_v1alpha1_Asset(&in.Asset, &out.Asset, s); err != nil { + return err + } + out.Args = *(*[]string)(unsafe.Pointer(&in.Args)) + return nil +} + +// Convert_v1alpha2_Kernel_To_v1alpha1_Kernel is an autogenerated conversion function. +func Convert_v1alpha2_Kernel_To_v1alpha1_Kernel(in *v1alpha2.Kernel, out *Kernel, s conversion.Scope) error { + return autoConvert_v1alpha2_Kernel_To_v1alpha1_Kernel(in, out, s) +} + +func autoConvert_v1alpha1_ManagementAPI_To_v1alpha2_ManagementAPI(in *ManagementAPI, out *v1alpha2.ManagementAPI, s conversion.Scope) error { + out.Endpoint = in.Endpoint + return nil +} + +// Convert_v1alpha1_ManagementAPI_To_v1alpha2_ManagementAPI is an autogenerated conversion function. +func Convert_v1alpha1_ManagementAPI_To_v1alpha2_ManagementAPI(in *ManagementAPI, out *v1alpha2.ManagementAPI, s conversion.Scope) error { + return autoConvert_v1alpha1_ManagementAPI_To_v1alpha2_ManagementAPI(in, out, s) +} + +func autoConvert_v1alpha2_ManagementAPI_To_v1alpha1_ManagementAPI(in *v1alpha2.ManagementAPI, out *ManagementAPI, s conversion.Scope) error { + out.Endpoint = in.Endpoint + return nil +} + +// Convert_v1alpha2_ManagementAPI_To_v1alpha1_ManagementAPI is an autogenerated conversion function. +func Convert_v1alpha2_ManagementAPI_To_v1alpha1_ManagementAPI(in *v1alpha2.ManagementAPI, out *ManagementAPI, s conversion.Scope) error { + return autoConvert_v1alpha2_ManagementAPI_To_v1alpha1_ManagementAPI(in, out, s) +} + +func autoConvert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers(in *Qualifiers, out *v1alpha2.Qualifiers, s conversion.Scope) error { + // WARNING: in.CPU requires manual conversion: does not exist in peer-type + // WARNING: in.SystemInformation requires manual conversion: does not exist in peer-type + out.LabelSelectors = *(*[]map[string]string)(unsafe.Pointer(&in.LabelSelectors)) + return nil +} + +func autoConvert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers(in *v1alpha2.Qualifiers, out *Qualifiers, s conversion.Scope) error { + // WARNING: in.Hardware requires manual conversion: does not exist in peer-type + out.LabelSelectors = *(*[]map[string]string)(unsafe.Pointer(&in.LabelSelectors)) + return nil +} + +func autoConvert_v1alpha1_SecretKeyRef_To_v1alpha2_SecretKeyRef(in *SecretKeyRef, out *v1alpha2.SecretKeyRef, s conversion.Scope) error { + out.Namespace = in.Namespace + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1alpha1_SecretKeyRef_To_v1alpha2_SecretKeyRef is an autogenerated conversion function. +func Convert_v1alpha1_SecretKeyRef_To_v1alpha2_SecretKeyRef(in *SecretKeyRef, out *v1alpha2.SecretKeyRef, s conversion.Scope) error { + return autoConvert_v1alpha1_SecretKeyRef_To_v1alpha2_SecretKeyRef(in, out, s) +} + +func autoConvert_v1alpha2_SecretKeyRef_To_v1alpha1_SecretKeyRef(in *v1alpha2.SecretKeyRef, out *SecretKeyRef, s conversion.Scope) error { + out.Namespace = in.Namespace + out.Name = in.Name + out.Key = in.Key + return nil +} + +// Convert_v1alpha2_SecretKeyRef_To_v1alpha1_SecretKeyRef is an autogenerated conversion function. +func Convert_v1alpha2_SecretKeyRef_To_v1alpha1_SecretKeyRef(in *v1alpha2.SecretKeyRef, out *SecretKeyRef, s conversion.Scope) error { + return autoConvert_v1alpha2_SecretKeyRef_To_v1alpha1_SecretKeyRef(in, out, s) +} + +func autoConvert_v1alpha1_Server_To_v1alpha2_Server(in *Server, out *v1alpha2.Server, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_Server_To_v1alpha2_Server is an autogenerated conversion function. +func Convert_v1alpha1_Server_To_v1alpha2_Server(in *Server, out *v1alpha2.Server, s conversion.Scope) error { + return autoConvert_v1alpha1_Server_To_v1alpha2_Server(in, out, s) +} + +func autoConvert_v1alpha2_Server_To_v1alpha1_Server(in *v1alpha2.Server, out *Server, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_Server_To_v1alpha1_Server is an autogenerated conversion function. +func Convert_v1alpha2_Server_To_v1alpha1_Server(in *v1alpha2.Server, out *Server, s conversion.Scope) error { + return autoConvert_v1alpha2_Server_To_v1alpha1_Server(in, out, s) +} + +func autoConvert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(in *ServerClass, out *v1alpha2.ServerClass, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ServerClass_To_v1alpha2_ServerClass is an autogenerated conversion function. +func Convert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(in *ServerClass, out *v1alpha2.ServerClass, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(in, out, s) +} + +func autoConvert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(in *v1alpha2.ServerClass, out *ServerClass, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ServerClass_To_v1alpha1_ServerClass is an autogenerated conversion function. +func Convert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(in *v1alpha2.ServerClass, out *ServerClass, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(in, out, s) +} + +func autoConvert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList(in *ServerClassList, out *v1alpha2.ServerClassList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ServerClass, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ServerClass_To_v1alpha2_ServerClass(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList is an autogenerated conversion function. +func Convert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList(in *ServerClassList, out *v1alpha2.ServerClassList, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerClassList_To_v1alpha2_ServerClassList(in, out, s) +} + +func autoConvert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList(in *v1alpha2.ServerClassList, out *ServerClassList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServerClass, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ServerClass_To_v1alpha1_ServerClass(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList is an autogenerated conversion function. +func Convert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList(in *v1alpha2.ServerClassList, out *ServerClassList, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerClassList_To_v1alpha1_ServerClassList(in, out, s) +} + +func autoConvert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec(in *ServerClassSpec, out *v1alpha2.ServerClassSpec, s conversion.Scope) error { + out.EnvironmentRef = (*v1.ObjectReference)(unsafe.Pointer(in.EnvironmentRef)) + if err := Convert_v1alpha1_Qualifiers_To_v1alpha2_Qualifiers(&in.Qualifiers, &out.Qualifiers, s); err != nil { + return err + } + out.Selector = in.Selector + out.ConfigPatches = *(*[]v1alpha2.ConfigPatches)(unsafe.Pointer(&in.ConfigPatches)) + out.BootFromDiskMethod = types.BootFromDisk(in.BootFromDiskMethod) + return nil +} + +// Convert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec is an autogenerated conversion function. +func Convert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec(in *ServerClassSpec, out *v1alpha2.ServerClassSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerClassSpec_To_v1alpha2_ServerClassSpec(in, out, s) +} + +func autoConvert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec(in *v1alpha2.ServerClassSpec, out *ServerClassSpec, s conversion.Scope) error { + out.EnvironmentRef = (*v1.ObjectReference)(unsafe.Pointer(in.EnvironmentRef)) + if err := Convert_v1alpha2_Qualifiers_To_v1alpha1_Qualifiers(&in.Qualifiers, &out.Qualifiers, s); err != nil { + return err + } + out.Selector = in.Selector + out.ConfigPatches = *(*[]ConfigPatches)(unsafe.Pointer(&in.ConfigPatches)) + out.BootFromDiskMethod = types.BootFromDisk(in.BootFromDiskMethod) + return nil +} + +// Convert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec is an autogenerated conversion function. +func Convert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec(in *v1alpha2.ServerClassSpec, out *ServerClassSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerClassSpec_To_v1alpha1_ServerClassSpec(in, out, s) +} + +func autoConvert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus(in *ServerClassStatus, out *v1alpha2.ServerClassStatus, s conversion.Scope) error { + out.ServersAvailable = *(*[]string)(unsafe.Pointer(&in.ServersAvailable)) + out.ServersInUse = *(*[]string)(unsafe.Pointer(&in.ServersInUse)) + return nil +} + +// Convert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus is an autogenerated conversion function. +func Convert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus(in *ServerClassStatus, out *v1alpha2.ServerClassStatus, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerClassStatus_To_v1alpha2_ServerClassStatus(in, out, s) +} + +func autoConvert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus(in *v1alpha2.ServerClassStatus, out *ServerClassStatus, s conversion.Scope) error { + out.ServersAvailable = *(*[]string)(unsafe.Pointer(&in.ServersAvailable)) + out.ServersInUse = *(*[]string)(unsafe.Pointer(&in.ServersInUse)) + return nil +} + +// Convert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus is an autogenerated conversion function. +func Convert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus(in *v1alpha2.ServerClassStatus, out *ServerClassStatus, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerClassStatus_To_v1alpha1_ServerClassStatus(in, out, s) +} + +func autoConvert_v1alpha1_ServerList_To_v1alpha2_ServerList(in *ServerList, out *v1alpha2.ServerList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.Server, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_Server_To_v1alpha2_Server(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ServerList_To_v1alpha2_ServerList is an autogenerated conversion function. +func Convert_v1alpha1_ServerList_To_v1alpha2_ServerList(in *ServerList, out *v1alpha2.ServerList, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerList_To_v1alpha2_ServerList(in, out, s) +} + +func autoConvert_v1alpha2_ServerList_To_v1alpha1_ServerList(in *v1alpha2.ServerList, out *ServerList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Server, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_Server_To_v1alpha1_Server(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ServerList_To_v1alpha1_ServerList is an autogenerated conversion function. +func Convert_v1alpha2_ServerList_To_v1alpha1_ServerList(in *v1alpha2.ServerList, out *ServerList, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerList_To_v1alpha1_ServerList(in, out, s) +} + +func autoConvert_v1alpha1_ServerSpec_To_v1alpha2_ServerSpec(in *ServerSpec, out *v1alpha2.ServerSpec, s conversion.Scope) error { + out.EnvironmentRef = (*v1.ObjectReference)(unsafe.Pointer(in.EnvironmentRef)) + out.Hostname = in.Hostname + // WARNING: in.SystemInformation requires manual conversion: does not exist in peer-type + // WARNING: in.CPU requires manual conversion: does not exist in peer-type + out.BMC = (*v1alpha2.BMC)(unsafe.Pointer(in.BMC)) + out.ManagementAPI = (*v1alpha2.ManagementAPI)(unsafe.Pointer(in.ManagementAPI)) + out.ConfigPatches = *(*[]v1alpha2.ConfigPatches)(unsafe.Pointer(&in.ConfigPatches)) + out.Accepted = in.Accepted + out.Cordoned = in.Cordoned + out.PXEBootAlways = in.PXEBootAlways + out.BootFromDiskMethod = types.BootFromDisk(in.BootFromDiskMethod) + return nil +} + +func autoConvert_v1alpha2_ServerSpec_To_v1alpha1_ServerSpec(in *v1alpha2.ServerSpec, out *ServerSpec, s conversion.Scope) error { + out.EnvironmentRef = (*v1.ObjectReference)(unsafe.Pointer(in.EnvironmentRef)) + // WARNING: in.Hardware requires manual conversion: does not exist in peer-type + out.Hostname = in.Hostname + out.BMC = (*BMC)(unsafe.Pointer(in.BMC)) + out.ManagementAPI = (*ManagementAPI)(unsafe.Pointer(in.ManagementAPI)) + out.ConfigPatches = *(*[]ConfigPatches)(unsafe.Pointer(&in.ConfigPatches)) + out.Accepted = in.Accepted + out.Cordoned = in.Cordoned + out.PXEBootAlways = in.PXEBootAlways + out.BootFromDiskMethod = types.BootFromDisk(in.BootFromDiskMethod) + return nil +} + +func autoConvert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus(in *ServerStatus, out *v1alpha2.ServerStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.InUse = in.InUse + out.IsClean = in.IsClean + out.Conditions = *(*[]v1beta1.Condition)(unsafe.Pointer(&in.Conditions)) + out.Addresses = *(*[]v1.NodeAddress)(unsafe.Pointer(&in.Addresses)) + out.Power = in.Power + return nil +} + +// Convert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus is an autogenerated conversion function. +func Convert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus(in *ServerStatus, out *v1alpha2.ServerStatus, s conversion.Scope) error { + return autoConvert_v1alpha1_ServerStatus_To_v1alpha2_ServerStatus(in, out, s) +} + +func autoConvert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus(in *v1alpha2.ServerStatus, out *ServerStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.InUse = in.InUse + out.IsClean = in.IsClean + out.Conditions = *(*[]v1beta1.Condition)(unsafe.Pointer(&in.Conditions)) + out.Addresses = *(*[]v1.NodeAddress)(unsafe.Pointer(&in.Addresses)) + out.Power = in.Power + return nil +} + +// Convert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus is an autogenerated conversion function. +func Convert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus(in *v1alpha2.ServerStatus, out *ServerStatus, s conversion.Scope) error { + return autoConvert_v1alpha2_ServerStatus_To_v1alpha1_ServerStatus(in, out, s) +} + +func autoConvert_v1alpha1_SystemInformation_To_v1alpha2_SystemInformation(in *SystemInformation, out *v1alpha2.SystemInformation, s conversion.Scope) error { + out.Manufacturer = in.Manufacturer + out.ProductName = in.ProductName + out.Version = in.Version + out.SerialNumber = in.SerialNumber + out.SKUNumber = in.SKUNumber + out.Family = in.Family + return nil +} + +// Convert_v1alpha1_SystemInformation_To_v1alpha2_SystemInformation is an autogenerated conversion function. +func Convert_v1alpha1_SystemInformation_To_v1alpha2_SystemInformation(in *SystemInformation, out *v1alpha2.SystemInformation, s conversion.Scope) error { + return autoConvert_v1alpha1_SystemInformation_To_v1alpha2_SystemInformation(in, out, s) +} + +func autoConvert_v1alpha2_SystemInformation_To_v1alpha1_SystemInformation(in *v1alpha2.SystemInformation, out *SystemInformation, s conversion.Scope) error { + // WARNING: in.Uuid requires manual conversion: does not exist in peer-type + out.Manufacturer = in.Manufacturer + out.ProductName = in.ProductName + out.Version = in.Version + out.SerialNumber = in.SerialNumber + out.SKUNumber = in.SKUNumber + out.Family = in.Family + return nil +} diff --git a/app/sidero-controller-manager/api/v1alpha2/doc.go b/app/sidero-controller-manager/api/v1alpha2/doc.go new file mode 100644 index 000000000..fd95a0726 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/doc.go @@ -0,0 +1,5 @@ +// 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 v1alpha2 diff --git a/app/sidero-controller-manager/api/v1alpha2/environment_conversion.go b/app/sidero-controller-manager/api/v1alpha2/environment_conversion.go new file mode 100644 index 000000000..cd8e4ebf9 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/environment_conversion.go @@ -0,0 +1,8 @@ +// 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 v1alpha2 + +func (*Environment) Hub() {} +func (*EnvironmentList) Hub() {} diff --git a/app/sidero-controller-manager/api/v1alpha2/environment_types.go b/app/sidero-controller-manager/api/v1alpha2/environment_types.go new file mode 100644 index 000000000..a9a01b056 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/environment_types.go @@ -0,0 +1,123 @@ +// 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 v1alpha2 + +import ( + "fmt" + "sort" + + "github.com/talos-systems/talos/pkg/machinery/kernel" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EnvironmentDefault is an automatically created Environment. +const EnvironmentDefault = "default" + +type Asset struct { + URL string `json:"url,omitempty"` + SHA512 string `json:"sha512,omitempty"` +} + +type Kernel struct { + Asset `json:",inline"` + + Args []string `json:"args,omitempty"` +} + +type Initrd struct { + Asset `json:",inline"` +} + +// EnvironmentSpec defines the desired state of Environment. +type EnvironmentSpec struct { + Kernel Kernel `json:"kernel,omitempty"` + Initrd Initrd `json:"initrd,omitempty"` +} + +type AssetCondition struct { + Asset `json:",inline"` + Status string `json:"status"` + Type string `json:"type"` +} + +// EnvironmentStatus defines the observed state of Environment. +type EnvironmentStatus struct { + Conditions []AssetCondition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Kernel",type="string",JSONPath=".spec.kernel.url",description="the kernel for the environment" +// +kubebuilder:printcolumn:name="Initrd",type="string",JSONPath=".spec.initrd.url",description="the initrd for the environment" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="indicates the readiness of the environment" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of this resource" +// +kubebuilder:storageversion + +// Environment is the Schema for the environments API. +type Environment struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec EnvironmentSpec `json:"spec,omitempty"` + Status EnvironmentStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// EnvironmentList contains a list of Environment. +type EnvironmentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Environment `json:"items"` +} + +// EnvironmentDefaultSpec returns EnvironmentDefault's spec. +func EnvironmentDefaultSpec(talosRelease, apiEndpoint string, apiPort uint16) *EnvironmentSpec { + args := make([]string, 0, len(kernel.DefaultArgs)+6) + args = append(args, kernel.DefaultArgs...) + args = append(args, "console=tty0", "console=ttyS0", "earlyprintk=ttyS0") + args = append(args, "initrd=initramfs.xz", "talos.platform=metal") + sort.Strings(args) + + return &EnvironmentSpec{ + Kernel: Kernel{ + Asset: Asset{ + URL: fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/vmlinuz-amd64", talosRelease), + }, + Args: args, + }, + Initrd: Initrd{ + Asset: Asset{ + URL: fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/initramfs-amd64.xz", talosRelease), + }, + }, + } +} + +// IsReady returns aggregated Environment readiness. +func (env *Environment) IsReady() bool { + assetURLs := map[string]struct{}{} + + if env.Spec.Kernel.URL != "" { + assetURLs[env.Spec.Kernel.URL] = struct{}{} + } + + if env.Spec.Initrd.URL != "" { + assetURLs[env.Spec.Initrd.URL] = struct{}{} + } + + for _, cond := range env.Status.Conditions { + if cond.Status == "True" && cond.Type == "Ready" { + delete(assetURLs, cond.URL) + } + } + + return len(assetURLs) == 0 +} + +func init() { + SchemeBuilder.Register(&Environment{}, &EnvironmentList{}) +} diff --git a/app/sidero-controller-manager/api/v1alpha2/environment_webhook.go b/app/sidero-controller-manager/api/v1alpha2/environment_webhook.go new file mode 100644 index 000000000..1b0ccf6f2 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/environment_webhook.go @@ -0,0 +1,21 @@ +// 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 v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *Environment) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +func (r *EnvironmentList) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} diff --git a/app/sidero-controller-manager/api/v1alpha2/groupversion_info.go b/app/sidero-controller-manager/api/v1alpha2/groupversion_info.go new file mode 100644 index 000000000..35a2bc3bc --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/groupversion_info.go @@ -0,0 +1,24 @@ +// 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 v1alpha2 contains API Schema definitions for the metal v1alpha2 API group +// +kubebuilder:object:generate=true +// +groupName=metal.sidero.dev +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "metal.sidero.dev", Version: "v1alpha2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/app/sidero-controller-manager/api/v1alpha2/server_conversion.go b/app/sidero-controller-manager/api/v1alpha2/server_conversion.go new file mode 100644 index 000000000..f32f528a2 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/server_conversion.go @@ -0,0 +1,8 @@ +// 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 v1alpha2 + +func (*Server) Hub() {} +func (*ServerList) Hub() {} diff --git a/app/sidero-controller-manager/api/v1alpha2/server_types.go b/app/sidero-controller-manager/api/v1alpha2/server_types.go new file mode 100644 index 000000000..c7ae5215a --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/server_types.go @@ -0,0 +1,329 @@ +// 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 v1alpha2 + +import ( + "context" + "fmt" + "reflect" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/client" + + siderotypes "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/types" +) + +// BMC defines data about how to talk to the node via ipmitool. +type BMC struct { + // BMC endpoint. + Endpoint string `json:"endpoint"` + // BMC port. Defaults to 623. + // +optional + Port uint32 `json:"port,omitempty"` + // BMC user value. + // +optional + User string `json:"user,omitempty"` + // Source for the user value. Cannot be used if User is not empty. + // +optional + UserFrom *CredentialSource `json:"userFrom,omitempty"` + // BMC password value. + // +optional + Pass string `json:"pass,omitempty"` + // Source for the password value. Cannot be used if Pass is not empty. + // +optional + PassFrom *CredentialSource `json:"passFrom,omitempty"` + // BMC Interface Type. Defaults to lanplus. + // +optional + Interface string `json:"interface,omitempty"` +} + +// CredentialSource defines a reference to the credential value. +type CredentialSource struct { + SecretKeyRef *SecretKeyRef `json:"secretKeyRef,omitempty"` +} + +// SecretKeyRef defines a ref to a given key within a secret. +type SecretKeyRef struct { + // Namespace and name of credential secret + // nb: can't use namespacedname here b/c it doesn't have json tags in the struct :( + Namespace string `json:"namespace"` + Name string `json:"name"` + // Key to select + Key string `json:"key"` +} + +// Resolve the value using the references. +func (source *CredentialSource) Resolve(ctx context.Context, reader client.Client) (string, error) { + if source == nil { + return "", nil + } + + if source.SecretKeyRef == nil { + return "", fmt.Errorf("missing secretKeyRef") + } + + var secrets corev1.Secret + + if err := reader.Get( + ctx, + types.NamespacedName{ + Namespace: source.SecretKeyRef.Namespace, + Name: source.SecretKeyRef.Name, + }, + &secrets, + ); err != nil { + return "", fmt.Errorf("error getting secret %q: %w", source.SecretKeyRef.Name, err) + } + + rawValue, ok := secrets.Data[source.SecretKeyRef.Key] + if !ok { + return "", fmt.Errorf("secret key %q is missing in secret %q", source.SecretKeyRef.Key, source.SecretKeyRef.Name) + } + + return string(rawValue), nil +} + +// ManagementAPI defines data about how to talk to the node via simple HTTP API. +type ManagementAPI struct { + Endpoint string `json:"endpoint"` +} + +type SystemInformation struct { + Uuid string `json:"uuid,omitempty"` + Manufacturer string `json:"manufacturer,omitempty"` + ProductName string `json:"productName,omitempty"` + Version string `json:"version,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + SKUNumber string `json:"skuNumber,omitempty"` + Family string `json:"family,omitempty"` +} + +type Processor struct { + Manufacturer string `json:"manufacturer,omitempty"` + ProductName string `json:"productName,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + // Speed is in megahertz (Mhz) + Speed uint32 `json:"speed,omitempty"` + CoreCount uint32 `json:"coreCount,omitempty"` + ThreadCount uint32 `json:"threadCount,omitempty"` +} + +type ComputeInformation struct { + TotalCoreCount uint32 `json:"totalCoreCount,omitempty"` + TotalThreadCount uint32 `json:"totalThreadCount,omitempty"` + ProcessorCount uint32 `json:"processorCount,omitempty"` + Processors []*Processor `json:"processors,omitempty"` +} + +type MemoryModule struct { + Manufacturer string `json:"manufacturer,omitempty"` + ProductName string `json:"productName,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + Type string `json:"type,omitempty"` + // Size is in megabytes (MB) + Size uint32 `json:"size,omitempty"` + // Speed is in megatransfers per second (MT/S) + Speed uint32 `json:"speed,omitempty"` +} + +type MemoryInformation struct { + TotalSize string `json:"totalSize,omitempty"` + ModuleCount uint32 `json:"moduleCount,omitempty"` + Modules []*MemoryModule `json:"modules,omitempty"` +} + +type StorageDevice struct { + Type string `json:"type,omitempty"` + // Size is in bytes + Size uint64 `json:"size,omitempty"` + Model string `json:"productName,omitempty"` + Serial string `json:"serialNumber,omitempty"` + Name string `json:"name,omitempty"` + DeviceName string `json:"deviceName,omitempty"` + UUID string `json:"uuid,omitempty"` + WWID string `json:"wwid,omitempty"` +} + +type StorageInformation struct { + TotalSize string `json:"totalSize,omitempty"` + DeviceCount uint32 `json:"deviceCount,omitempty"` + Devices []*StorageDevice `json:"devices,omitempty"` +} + +type NetworkInterface struct { + Index uint32 `json:"index,omitempty"` + Name string `json:"name,omitempty"` + Flags string `json:"flags,omitempty"` + MTU uint32 `json:"mtu,omitempty"` + MAC string `json:"mac,omitempty"` + Addresses []string `json:"addresses,omitempty"` +} + +type NetworkInformation struct { + InterfaceCount uint32 `json:"interfaceCount,omitempty"` + Interfaces []*NetworkInterface `json:"interfaces,omitempty"` +} + +type HardwareInformation struct { + System *SystemInformation `json:"system,omitempty"` + Compute *ComputeInformation `json:"compute,omitempty"` + Memory *MemoryInformation `json:"memory,omitempty"` + Storage *StorageInformation `json:"storage,omitempty"` + Network *NetworkInformation `json:"network,omitempty"` +} + +func (a *HardwareInformation) PartialEqual(b *HardwareInformation) bool { + return PartialEqual(a, b) +} + +func PartialEqual(a, b interface{}) bool { + old := reflect.ValueOf(a) + new := reflect.ValueOf(b) + + if old.Kind() == reflect.Ptr { + old = old.Elem() + } + + if new.Kind() == reflect.Ptr { + new = new.Elem() + } + + // Skip invalid or zero values, since that indicates that the user + // did not supply the field, and does not want to compare it. + if !old.IsValid() || old.IsZero() { + return true + } + + switch { + case old.Kind() == reflect.Struct && new.Kind() == reflect.Struct: + // Recursively compare structs + for i := 0; i < old.NumField(); i++ { + f1 := old.Field(i).Interface() + f2 := new.Field(i).Interface() + + if !PartialEqual(f1, f2) { + return false + } + } + case old.Kind() == reflect.Slice && new.Kind() == reflect.Slice: + // Skip slices where the requested slice is larger than the actual slice, + // as that indicates that the user wants to filter for more + // processors/memory modules/storage devices than are present. + if old.Len() > new.Len() { + return false + } + // Recursively compare slices + for i := 0; i < old.Len(); i++ { + f1 := old.Index(i).Interface() + f2 := new.Index(i).Interface() + + if !PartialEqual(f1, f2) { + return false + } + } + default: + // Directly compare values, but only if the actual value is valid. + return new.IsValid() && old.Interface() == new.Interface() + } + + return true +} + +// ServerSpec defines the desired state of Server. +type ServerSpec struct { + EnvironmentRef *corev1.ObjectReference `json:"environmentRef,omitempty"` + Hardware *HardwareInformation `json:"hardware,omitempty"` + Hostname string `json:"hostname,omitempty"` + BMC *BMC `json:"bmc,omitempty"` + ManagementAPI *ManagementAPI `json:"managementApi,omitempty"` + ConfigPatches []ConfigPatches `json:"configPatches,omitempty"` + Accepted bool `json:"accepted"` + Cordoned bool `json:"cordoned,omitempty"` + PXEBootAlways bool `json:"pxeBootAlways,omitempty"` + // BootFromDiskMethod specifies the method to exit iPXE to force boot from disk. + // + // If not set, controller default is used. + // Valid values: ipxe-exit, http-404, ipxe-sanboot. + // + // +optional + BootFromDiskMethod siderotypes.BootFromDisk `json:"bootFromDiskMethod,omitempty"` +} + +const ( + // ConditionPowerCycle is used to control the powercycle flow. + ConditionPowerCycle clusterv1.ConditionType = "PowerCycle" + // ConditionPXEBooted is used to record the fact that server got PXE booted. + ConditionPXEBooted clusterv1.ConditionType = "PXEBooted" +) + +// ServerStatus defines the observed state of Server. +type ServerStatus struct { + // Ready is true when server is accepted and in use. + // +optional + Ready bool `json:"ready"` + + // InUse is true when server is assigned to some MetalMachine. + // +optional + InUse bool `json:"inUse"` + + // IsClean is true when server disks are wiped. + // +optional + IsClean bool `json:"isClean"` + + // Conditions defines current service state of the Server. + Conditions []clusterv1.Condition `json:"conditions,omitempty"` + + // Addresses lists discovered node IPs. + Addresses []corev1.NodeAddress `json:"addresses,omitempty"` + + // Power is the current power state of the server: "on", "off" or "unknown". + Power string `json:"power,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Hostname",type="string",JSONPath=".spec.hostname",description="server hostname" +// +kubebuilder:printcolumn:name="BMC IP",type="string",priority=1,JSONPath=".spec.bmc.endpoint",description="BMC IP" +// +kubebuilder:printcolumn:name="Accepted",type="boolean",JSONPath=".spec.accepted",description="indicates if the server is accepted" +// +kubebuilder:printcolumn:name="Cordoned",type="boolean",JSONPath=".spec.cordoned",description="indicates if the server is cordoned" +// +kubebuilder:printcolumn:name="Allocated",type="boolean",JSONPath=".status.inUse",description="indicates that the server has been allocated" +// +kubebuilder:printcolumn:name="Clean",type="boolean",JSONPath=".status.isClean",description="indicates if the server is clean or not" +// +kubebuilder:printcolumn:name="Power",type="string",JSONPath=".status.power",description="display the current power status" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of this resource" +// +kubebuilder:storageversion + +// Server is the Schema for the servers API. +type Server struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServerSpec `json:"spec,omitempty"` + Status ServerStatus `json:"status,omitempty"` +} + +func (s *Server) GetConditions() clusterv1.Conditions { + return s.Status.Conditions +} + +func (s *Server) SetConditions(conditions clusterv1.Conditions) { + s.Status.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// ServerList contains a list of Server. +type ServerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Server `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Server{}, &ServerList{}) +} diff --git a/app/sidero-controller-manager/api/v1alpha2/server_types_test.go b/app/sidero-controller-manager/api/v1alpha2/server_types_test.go new file mode 100644 index 000000000..92d0fe9c8 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/server_types_test.go @@ -0,0 +1,147 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//nolint:scopelint +package v1alpha2_test + +import ( + "testing" + + metal "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +func Test_PartialEqual(t *testing.T) { + info := &metal.HardwareInformation{ + System: &metal.SystemInformation{ + Uuid: "4c4c4544-0039-3010-8048-b7c04f384432", + Manufacturer: "Dell Inc.", + ProductName: "PowerEdge R630", + SerialNumber: "790H8D2", + SKUNumber: "", + Family: "", + }, + Compute: &metal.ComputeInformation{ + TotalCoreCount: 8, + TotalThreadCount: 16, + ProcessorCount: 1, + Processors: []*metal.Processor{ + { + Manufacturer: "Intel", + ProductName: "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + SerialNumber: "", + Speed: 2400, + CoreCount: 8, + ThreadCount: 16, + }, + }, + }, + Memory: &metal.MemoryInformation{ + TotalSize: "32 GB", + ModuleCount: 2, + Modules: []*metal.MemoryModule{ + { + Manufacturer: "002C00B3002C", + ProductName: "18ASF2G72PDZ-2G3B1", + SerialNumber: "12BDC045", + Type: "LPDDR3", + Size: 16384, + Speed: 2400, + }, + { + Manufacturer: "002C00B3002C", + ProductName: "18ASF2G72PDZ-2G3B1", + SerialNumber: "12BDBF5D", + Type: "LPDDR3", + Size: 16384, + Speed: 2400, + }, + }, + }, + Storage: &metal.StorageInformation{ + TotalSize: "1116 GB", + DeviceCount: 1, + Devices: []*metal.StorageDevice{ + { + Type: "HDD", + Size: 1199101181952, + Model: "PERC H730 Mini", + Serial: "", + Name: "sda", + DeviceName: "/dev/sda", + UUID: "", + WWID: "naa.61866da055de070028d8e83307cc6df2", + }, + }, + }, + Network: &metal.NetworkInformation{ + InterfaceCount: 2, + Interfaces: []*metal.NetworkInterface{ + { + Index: 1, + Name: "lo", + Flags: "up|loopback", + MTU: 65536, + MAC: "", + Addresses: []string{"127.0.0.1/8", "::1/128"}, + }, + { + Index: 2, + Name: "enp3s0", + Flags: "up|broadcast|multicast", + MTU: 1500, + MAC: "40:8d:5c:86:5a:14", + Addresses: []string{"192.168.2.8/24", "fe80::dcb3:295c:755b:91bb/64"}, + }, + }, + }, + } + + tests := []struct { + name string + args *metal.HardwareInformation + want bool + }{ + { + name: "defaults are partially equal", + args: &metal.HardwareInformation{}, + want: true, + }, + { + name: "cpu is partially equal", + args: &metal.HardwareInformation{ + Compute: &metal.ComputeInformation{ + Processors: []*metal.Processor{ + { + Manufacturer: "Intel", + }, + }, + }, + // Skip all other fields to indicate that we don't want to compare it. + }, + want: true, + }, + { + name: "cpu is not partially equal", + args: &metal.HardwareInformation{ + Compute: &metal.ComputeInformation{ + Processors: []*metal.Processor{ + { + Manufacturer: "AMD", + }, + }, + }, + // Skip all other fields to indicate that we don't want to compare it. + }, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.args.PartialEqual(info); got != tt.want { + t.Errorf("PartialEqual() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/app/sidero-controller-manager/api/v1alpha2/server_webhook.go b/app/sidero-controller-manager/api/v1alpha2/server_webhook.go new file mode 100644 index 000000000..83125f5ab --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/server_webhook.go @@ -0,0 +1,118 @@ +// 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 v1alpha2 + +import ( + "fmt" + "sort" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + siderotypes "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/types" +) + +var operations = map[string]struct{}{ + "add": {}, + "remove": {}, + "replace": {}, + "copy": {}, + "move": {}, + "test": {}, +} + +var operationKinds = []string{} + +func (r *ServerList) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +func (r *Server) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-metal-sidero-dev-v1alpha2-server,mutating=false,failurePolicy=fail,groups=metal.sidero.dev,resources=servers,versions=v1alpha2,name=vservers.metal.sidero.dev,sideEffects=None,admissionReviewVersions=v1 + +var _ webhook.Validator = &Server{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. +func (r *Server) ValidateCreate() error { + return r.validate() +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. +func (r *Server) ValidateUpdate(old runtime.Object) error { + return r.validate() +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. +func (r *Server) ValidateDelete() error { + return nil +} + +func (r *Server) validate() error { + var allErrs field.ErrorList + + validValues := []siderotypes.BootFromDisk{ + "", + siderotypes.BootIPXEExit, + siderotypes.Boot404, + siderotypes.BootSANDisk, + } + + var valid bool + + for _, v := range validValues { + if r.Spec.BootFromDiskMethod == v { + valid = true + + break + } + } + + if !valid { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("bootFromDiskMethod"), r.Spec.BootFromDiskMethod, + fmt.Sprintf("valid values are: %q", validValues), + ), + ) + } + + for index, patch := range r.Spec.ConfigPatches { + if _, ok := operations[patch.Op]; !ok { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("configPatches").Child(fmt.Sprintf("%d", index)).Child("op"), patch.Op, + fmt.Sprintf("valid values are: %q", operationKinds), + ), + ) + } + } + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid( + schema.GroupKind{Group: GroupVersion.Group, Kind: "Server"}, + r.Name, allErrs) +} + +func init() { + operationKinds = make([]string, 0, len(operations)) + + for key := range operations { + operationKinds = append(operationKinds, key) + } + + sort.Strings(operationKinds) +} diff --git a/app/sidero-controller-manager/api/v1alpha2/serverclass_conversion.go b/app/sidero-controller-manager/api/v1alpha2/serverclass_conversion.go new file mode 100644 index 000000000..dbeb291af --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/serverclass_conversion.go @@ -0,0 +1,8 @@ +// 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 v1alpha2 + +func (*ServerClass) Hub() {} +func (*ServerClassList) Hub() {} diff --git a/app/sidero-controller-manager/api/v1alpha2/serverclass_filter.go b/app/sidero-controller-manager/api/v1alpha2/serverclass_filter.go new file mode 100644 index 000000000..f5ce08250 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/serverclass_filter.go @@ -0,0 +1,110 @@ +// 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 v1alpha2 + +import ( + "fmt" + "sort" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// AcceptedServerFilter matches Servers that have Spec.Accepted set to true. +func AcceptedServerFilter(s Server) (bool, error) { + return s.Spec.Accepted, nil +} + +// NotCordonedServerFilter matches Servers that have Spec.Paused set to false. +func NotCordonedServerFilter(s Server) (bool, error) { + return !s.Spec.Cordoned, nil +} + +// SelectorFilter returns a ServerFilter that matches servers against the +// serverclass's selector field. +func (sc *ServerClass) SelectorFilter() func(Server) (bool, error) { + return func(server Server) (bool, error) { + s, err := metav1.LabelSelectorAsSelector(&sc.Spec.Selector) + if err != nil { + return false, fmt.Errorf("failed to get selector from labelselector: %v", err) + } + + return s.Matches(labels.Set(server.GetLabels())), nil + } +} + +// QualifiersFilter returns a ServerFilter that matches servers against the +// serverclass's qualifiers field. +func (sc *ServerClass) QualifiersFilter() func(Server) (bool, error) { + return func(server Server) (bool, error) { + q := sc.Spec.Qualifiers + + // check hardware qualifiers if they are present + if filters := q.Hardware; len(filters) > 0 { + var match bool + + for _, filter := range filters { + if info := server.Spec.Hardware; info != nil && filter.PartialEqual(info) { + match = true + break + } + } + + if !match { + return false, nil + } + } + + if filters := q.LabelSelectors; len(filters) > 0 { + var match bool + + for _, filter := range filters { + for labelKey, labelVal := range filter { + if val, ok := server.ObjectMeta.Labels[labelKey]; ok && labelVal == val { + match = true + break + } + } + } + + if !match { + return false, nil + } + } + + return true, nil + } +} + +// FilterServers returns the subset of servers that pass all provided filters. +// In case of error the returned slice will be nil. +func FilterServers(servers []Server, filters ...func(Server) (bool, error)) ([]Server, error) { + matches := make([]Server, 0, len(servers)) + + for _, server := range servers { + match := true + + for _, filter := range filters { + var err error + + match, err = filter(server) + if err != nil { + return nil, fmt.Errorf("failed to filter server: %v", err) + } + + if !match { + break + } + } + + if match { + matches = append(matches, server) + } + } + + sort.Slice(matches, func(i, j int) bool { return matches[i].Name < matches[j].Name }) + + return matches, nil +} diff --git a/app/sidero-controller-manager/api/v1alpha2/serverclass_filter_test.go b/app/sidero-controller-manager/api/v1alpha2/serverclass_filter_test.go new file mode 100644 index 000000000..125bec1dc --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/serverclass_filter_test.go @@ -0,0 +1,378 @@ +// 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 v1alpha2_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + metal "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" +) + +func TestFilterAcceptedServers(t *testing.T) { + t.Parallel() + + atom := metal.Server{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "common-label": "true", + "zone": "central", + }, + }, + Spec: metal.ServerSpec{ + Accepted: true, + Hardware: &metal.HardwareInformation{ + Compute: &metal.ComputeInformation{ + TotalCoreCount: 4, + TotalThreadCount: 4, + ProcessorCount: 1, + Processors: []*metal.Processor{ + { + Manufacturer: "Intel(R) Corporation", + ProductName: "Intel(R) Atom(TM) CPU C3558 @ 2.20GHz", + SerialNumber: "", + Speed: 2200, + CoreCount: 4, + ThreadCount: 4, + }, + }, + }, + }, + }, + } + dualXeon := metal.Server{ + Spec: metal.ServerSpec{ + Accepted: true, + Hardware: &metal.HardwareInformation{ + Compute: &metal.ComputeInformation{ + TotalCoreCount: 16, + TotalThreadCount: 32, + ProcessorCount: 2, + Processors: []*metal.Processor{ + { + Manufacturer: "Intel(R) Corporation", + ProductName: "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + SerialNumber: "", + Speed: 2400, + CoreCount: 8, + ThreadCount: 16, + }, + { + Manufacturer: "Intel(R) Corporation", + ProductName: "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + SerialNumber: "", + Speed: 2400, + CoreCount: 8, + ThreadCount: 16, + }, + }, + }, + }, + }, + } + ryzen := metal.Server{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "common-label": "true", + "zone": "east", + }, + }, + Spec: metal.ServerSpec{ + Accepted: true, + Hardware: &metal.HardwareInformation{ + System: &metal.SystemInformation{ + Manufacturer: "QEMU", + }, + Compute: &metal.ComputeInformation{ + TotalCoreCount: 8, + TotalThreadCount: 16, + ProcessorCount: 1, + Processors: []*metal.Processor{ + { + Manufacturer: "Advanced Micro Devices, Inc.", + ProductName: "AMD Ryzen 7 2700X Eight-Core Processor", + SerialNumber: "", + Speed: 3700, + CoreCount: 8, + ThreadCount: 16, + }, + }, + }, + }, + }, + } + notAccepted := metal.Server{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "common-label": "true", + "zone": "west", + }, + }, + Spec: metal.ServerSpec{ + Accepted: false, + Hardware: &metal.HardwareInformation{ + System: &metal.SystemInformation{ + Manufacturer: "QEMU", + }, + Compute: &metal.ComputeInformation{ + TotalCoreCount: 8, + TotalThreadCount: 16, + ProcessorCount: 1, + Processors: []*metal.Processor{ + { + Manufacturer: "Advanced Micro Devices, Inc.", + ProductName: "AMD Ryzen 7 2700X Eight-Core Processor", + SerialNumber: "", + Speed: 3700, + CoreCount: 8, + ThreadCount: 16, + }, + }, + }, + }, + }, + } + + servers := []metal.Server{atom, dualXeon, ryzen, notAccepted} + + testdata := map[string]struct { + s metav1.LabelSelector + q metal.Qualifiers + expected []metal.Server + }{ + "empty selector - empty qualifier": { + // Matches all servers + expected: []metal.Server{atom, dualXeon, ryzen}, + }, + "Intel only": { + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + Compute: &metal.ComputeInformation{ + Processors: []*metal.Processor{ + { + Manufacturer: "Intel(R) Corporation", + }, + }, + }, + }, + }, + }, + expected: []metal.Server{atom, dualXeon}, + }, + "Intel and AMD": { + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + Compute: &metal.ComputeInformation{ + Processors: []*metal.Processor{ + { + Manufacturer: "Intel(R) Corporation", + }, + }, + }, + }, + { + Compute: &metal.ComputeInformation{ + Processors: []*metal.Processor{ + { + Manufacturer: "Advanced Micro Devices, Inc.", + }, + }, + }, + }, + }, + }, + expected: []metal.Server{atom, dualXeon, ryzen}, + }, + "QEMU only": { + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + System: &metal.SystemInformation{ + Manufacturer: "QEMU", + }, + }, + }, + }, + expected: []metal.Server{ryzen}, + }, + "with label": { + q: metal.Qualifiers{ + LabelSelectors: []map[string]string{ + { + "common-label": "true", + }, + }, + }, + expected: []metal.Server{atom, ryzen}, + }, + // This should probably only return atom. Leaving it as-is to + // avoid breaking changes before we remove LabelSelectors in + // favor of Selector. + "with multiple labels - single selector": { + q: metal.Qualifiers{ + LabelSelectors: []map[string]string{ + { + "common-label": "true", + "zone": "central", + }, + }, + }, + expected: []metal.Server{atom, ryzen}, + }, + "with multiple labels - multiple selectors": { + q: metal.Qualifiers{ + LabelSelectors: []map[string]string{ + { + "common-label": "true", + }, + { + "zone": "central", + }, + }, + }, + expected: []metal.Server{atom, ryzen}, + }, + "with same label key different label value": { + q: metal.Qualifiers{ + LabelSelectors: []map[string]string{ + { + "zone": "central", + }, + }, + }, + expected: []metal.Server{atom}, + }, + "selector - single MatchLabels single result": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "zone": "central", + }, + }, + expected: []metal.Server{atom}, + }, + "selector - single MatchLabels multiple results": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "common-label": "true", + }, + }, + expected: []metal.Server{atom, ryzen}, + }, + "selector - multiple MatchLabels": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "zone": "central", + "common-label": "true", + }, + }, + expected: []metal.Server{atom}, + }, + "selector - MatchExpressions common label key": { + s: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "common-label", + Operator: "Exists", + }, + }, + }, + expected: []metal.Server{atom, ryzen}, + }, + "selector - MatchExpressions multiple values": { + s: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "zone", + Operator: "In", + Values: []string{ + "east", + "west", + }, + }, + }, + }, + expected: []metal.Server{ryzen}, + }, + "selector and qualifiers both match": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "common-label": "true", + }, + }, + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + System: &metal.SystemInformation{ + Manufacturer: "QEMU", + }, + }, + }, + }, + expected: []metal.Server{ryzen}, + }, + "selector and qualifiers with disqualifying selector": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "common-label": "no-match", + }, + }, + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + System: &metal.SystemInformation{ + Manufacturer: "QEMU", + }, + }, + }, + }, + expected: []metal.Server{}, + }, + "selector and qualifiers with disqualifying qualifier": { + s: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "common-label": "true", + }, + }, + q: metal.Qualifiers{ + Hardware: []metal.HardwareInformation{ + { + System: &metal.SystemInformation{ + Manufacturer: "Gateway", + }, + }, + }, + }, + expected: []metal.Server{}, + }, + metal.ServerClassAny: { + expected: []metal.Server{atom, dualXeon, ryzen}, + }, + } + + for name, td := range testdata { + name, td := name, td + t.Run(name, func(t *testing.T) { + t.Parallel() + + sc := &metal.ServerClass{ + Spec: metal.ServerClassSpec{ + Selector: td.s, + Qualifiers: td.q, + }, + } + actual, err := metal.FilterServers(servers, + metal.AcceptedServerFilter, + sc.SelectorFilter(), + sc.QualifiersFilter(), + ) + assert.NoError(t, err) + assert.Equal(t, td.expected, actual) + }) + } +} diff --git a/app/sidero-controller-manager/api/v1alpha2/serverclass_types.go b/app/sidero-controller-manager/api/v1alpha2/serverclass_types.go new file mode 100644 index 000000000..45ea2ba92 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/serverclass_types.go @@ -0,0 +1,84 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package v1alpha2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + siderotypes "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/types" +) + +// ServerClassAny is an automatically created ServerClass that includes all Servers. +const ServerClassAny = "any" + +type Qualifiers struct { + Hardware []HardwareInformation `json:"hardware,omitempty"` + LabelSelectors []map[string]string `json:"labelSelectors,omitempty"` +} + +// ServerClassSpec defines the desired state of ServerClass. +type ServerClassSpec struct { + // Reference to the environment which should be used to provision the servers via this server class. + // +optional + EnvironmentRef *corev1.ObjectReference `json:"environmentRef,omitempty"` + // Qualifiers to match on the server spec. + // + // If qualifiers are empty, they match all servers. + // Server should match both qualifiers and selector conditions to be included into the server class. + // +optional + Qualifiers Qualifiers `json:"qualifiers"` + // Label selector to filter the matching servers based on labels. + // A label selector is a label query over a set of resources. The result of matchLabels and + // matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. + // +optional + Selector metav1.LabelSelector `json:"selector"` + // Set of config patches to apply to the machine configuration to the servers provisioned via this server class. + // +optional + ConfigPatches []ConfigPatches `json:"configPatches,omitempty"` + // BootFromDiskMethod specifies the method to exit iPXE to force boot from disk. + // + // If not set, controller default is used. + // Valid values: ipxe-exit, http-404, ipxe-sanboot. + // + // +optional + BootFromDiskMethod siderotypes.BootFromDisk `json:"bootFromDiskMethod,omitempty"` +} + +// ServerClassStatus defines the observed state of ServerClass. +type ServerClassStatus struct { + ServersAvailable []string `json:"serversAvailable"` + ServersInUse []string `json:"serversInUse"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Available",type="string",JSONPath=".status.serversAvailable",description="the number of available servers" +// +kubebuilder:printcolumn:name="In Use",type="string",JSONPath=".status.serversInUse",description="the number of servers in use" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of this resource" +// +kubebuilder:storageversion + +// ServerClass is the Schema for the serverclasses API. +type ServerClass struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServerClassSpec `json:"spec,omitempty"` + Status ServerClassStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ServerClassList contains a list of ServerClass. +type ServerClassList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServerClass `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ServerClass{}, &ServerClassList{}) +} diff --git a/app/sidero-controller-manager/api/v1alpha2/serverclass_webhook.go b/app/sidero-controller-manager/api/v1alpha2/serverclass_webhook.go new file mode 100644 index 000000000..45aa1eff5 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/serverclass_webhook.go @@ -0,0 +1,21 @@ +// 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 v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *ServerClass) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +func (r *ServerClassList) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} diff --git a/app/sidero-controller-manager/api/v1alpha2/types.go b/app/sidero-controller-manager/api/v1alpha2/types.go new file mode 100644 index 000000000..80525d27d --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/types.go @@ -0,0 +1,16 @@ +// 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 v1alpha2 + +import apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + +// nb: we use apiextensions.JSON for the value below b/c we can't use interface{} with controller-gen. +// found this workaround here: https://github.com/kubernetes-sigs/controller-tools/pull/126#issuecomment-630769075 + +type ConfigPatches struct { + Op string `json:"op"` + Path string `json:"path"` + Value apiextensions.JSON `json:"value,omitempty"` +} diff --git a/app/sidero-controller-manager/api/v1alpha2/zz_generated.deepcopy.go b/app/sidero-controller-manager/api/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 000000000..b27db6381 --- /dev/null +++ b/app/sidero-controller-manager/api/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,771 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// 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/. + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Asset) DeepCopyInto(out *Asset) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Asset. +func (in *Asset) DeepCopy() *Asset { + if in == nil { + return nil + } + out := new(Asset) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AssetCondition) DeepCopyInto(out *AssetCondition) { + *out = *in + out.Asset = in.Asset +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AssetCondition. +func (in *AssetCondition) DeepCopy() *AssetCondition { + if in == nil { + return nil + } + out := new(AssetCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BMC) DeepCopyInto(out *BMC) { + *out = *in + if in.UserFrom != nil { + in, out := &in.UserFrom, &out.UserFrom + *out = new(CredentialSource) + (*in).DeepCopyInto(*out) + } + if in.PassFrom != nil { + in, out := &in.PassFrom, &out.PassFrom + *out = new(CredentialSource) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BMC. +func (in *BMC) DeepCopy() *BMC { + if in == nil { + return nil + } + out := new(BMC) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComputeInformation) DeepCopyInto(out *ComputeInformation) { + *out = *in + if in.Processors != nil { + in, out := &in.Processors, &out.Processors + *out = make([]*Processor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Processor) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComputeInformation. +func (in *ComputeInformation) DeepCopy() *ComputeInformation { + if in == nil { + return nil + } + out := new(ComputeInformation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigPatches) DeepCopyInto(out *ConfigPatches) { + *out = *in + in.Value.DeepCopyInto(&out.Value) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigPatches. +func (in *ConfigPatches) DeepCopy() *ConfigPatches { + if in == nil { + return nil + } + out := new(ConfigPatches) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CredentialSource) DeepCopyInto(out *CredentialSource) { + *out = *in + if in.SecretKeyRef != nil { + in, out := &in.SecretKeyRef, &out.SecretKeyRef + *out = new(SecretKeyRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialSource. +func (in *CredentialSource) DeepCopy() *CredentialSource { + if in == nil { + return nil + } + out := new(CredentialSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Environment) DeepCopyInto(out *Environment) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Environment. +func (in *Environment) DeepCopy() *Environment { + if in == nil { + return nil + } + out := new(Environment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Environment) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentList) DeepCopyInto(out *EnvironmentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Environment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentList. +func (in *EnvironmentList) DeepCopy() *EnvironmentList { + if in == nil { + return nil + } + out := new(EnvironmentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvironmentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) { + *out = *in + in.Kernel.DeepCopyInto(&out.Kernel) + out.Initrd = in.Initrd +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec. +func (in *EnvironmentSpec) DeepCopy() *EnvironmentSpec { + if in == nil { + return nil + } + out := new(EnvironmentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentStatus) DeepCopyInto(out *EnvironmentStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]AssetCondition, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentStatus. +func (in *EnvironmentStatus) DeepCopy() *EnvironmentStatus { + if in == nil { + return nil + } + out := new(EnvironmentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HardwareInformation) DeepCopyInto(out *HardwareInformation) { + *out = *in + if in.System != nil { + in, out := &in.System, &out.System + *out = new(SystemInformation) + **out = **in + } + if in.Compute != nil { + in, out := &in.Compute, &out.Compute + *out = new(ComputeInformation) + (*in).DeepCopyInto(*out) + } + if in.Memory != nil { + in, out := &in.Memory, &out.Memory + *out = new(MemoryInformation) + (*in).DeepCopyInto(*out) + } + if in.Storage != nil { + in, out := &in.Storage, &out.Storage + *out = new(StorageInformation) + (*in).DeepCopyInto(*out) + } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(NetworkInformation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HardwareInformation. +func (in *HardwareInformation) DeepCopy() *HardwareInformation { + if in == nil { + return nil + } + out := new(HardwareInformation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Initrd) DeepCopyInto(out *Initrd) { + *out = *in + out.Asset = in.Asset +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Initrd. +func (in *Initrd) DeepCopy() *Initrd { + if in == nil { + return nil + } + out := new(Initrd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Kernel) DeepCopyInto(out *Kernel) { + *out = *in + out.Asset = in.Asset + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kernel. +func (in *Kernel) DeepCopy() *Kernel { + if in == nil { + return nil + } + out := new(Kernel) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementAPI) DeepCopyInto(out *ManagementAPI) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementAPI. +func (in *ManagementAPI) DeepCopy() *ManagementAPI { + if in == nil { + return nil + } + out := new(ManagementAPI) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemoryInformation) DeepCopyInto(out *MemoryInformation) { + *out = *in + if in.Modules != nil { + in, out := &in.Modules, &out.Modules + *out = make([]*MemoryModule, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(MemoryModule) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemoryInformation. +func (in *MemoryInformation) DeepCopy() *MemoryInformation { + if in == nil { + return nil + } + out := new(MemoryInformation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemoryModule) DeepCopyInto(out *MemoryModule) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemoryModule. +func (in *MemoryModule) DeepCopy() *MemoryModule { + if in == nil { + return nil + } + out := new(MemoryModule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkInformation) DeepCopyInto(out *NetworkInformation) { + *out = *in + if in.Interfaces != nil { + in, out := &in.Interfaces, &out.Interfaces + *out = make([]*NetworkInterface, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NetworkInterface) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkInformation. +func (in *NetworkInformation) DeepCopy() *NetworkInformation { + if in == nil { + return nil + } + out := new(NetworkInformation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkInterface) DeepCopyInto(out *NetworkInterface) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkInterface. +func (in *NetworkInterface) DeepCopy() *NetworkInterface { + if in == nil { + return nil + } + out := new(NetworkInterface) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Processor) DeepCopyInto(out *Processor) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Processor. +func (in *Processor) DeepCopy() *Processor { + if in == nil { + return nil + } + out := new(Processor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Qualifiers) DeepCopyInto(out *Qualifiers) { + *out = *in + if in.Hardware != nil { + in, out := &in.Hardware, &out.Hardware + *out = make([]HardwareInformation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LabelSelectors != nil { + in, out := &in.LabelSelectors, &out.LabelSelectors + *out = make([]map[string]string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Qualifiers. +func (in *Qualifiers) DeepCopy() *Qualifiers { + if in == nil { + return nil + } + out := new(Qualifiers) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretKeyRef) DeepCopyInto(out *SecretKeyRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeyRef. +func (in *SecretKeyRef) DeepCopy() *SecretKeyRef { + if in == nil { + return nil + } + out := new(SecretKeyRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Server) DeepCopyInto(out *Server) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Server. +func (in *Server) DeepCopy() *Server { + if in == nil { + return nil + } + out := new(Server) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Server) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerClass) DeepCopyInto(out *ServerClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerClass. +func (in *ServerClass) DeepCopy() *ServerClass { + if in == nil { + return nil + } + out := new(ServerClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServerClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerClassList) DeepCopyInto(out *ServerClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServerClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerClassList. +func (in *ServerClassList) DeepCopy() *ServerClassList { + if in == nil { + return nil + } + out := new(ServerClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServerClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerClassSpec) DeepCopyInto(out *ServerClassSpec) { + *out = *in + if in.EnvironmentRef != nil { + in, out := &in.EnvironmentRef, &out.EnvironmentRef + *out = new(v1.ObjectReference) + **out = **in + } + in.Qualifiers.DeepCopyInto(&out.Qualifiers) + in.Selector.DeepCopyInto(&out.Selector) + if in.ConfigPatches != nil { + in, out := &in.ConfigPatches, &out.ConfigPatches + *out = make([]ConfigPatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerClassSpec. +func (in *ServerClassSpec) DeepCopy() *ServerClassSpec { + if in == nil { + return nil + } + out := new(ServerClassSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerClassStatus) DeepCopyInto(out *ServerClassStatus) { + *out = *in + if in.ServersAvailable != nil { + in, out := &in.ServersAvailable, &out.ServersAvailable + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ServersInUse != nil { + in, out := &in.ServersInUse, &out.ServersInUse + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerClassStatus. +func (in *ServerClassStatus) DeepCopy() *ServerClassStatus { + if in == nil { + return nil + } + out := new(ServerClassStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerList) DeepCopyInto(out *ServerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Server, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerList. +func (in *ServerList) DeepCopy() *ServerList { + if in == nil { + return nil + } + out := new(ServerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerSpec) DeepCopyInto(out *ServerSpec) { + *out = *in + if in.EnvironmentRef != nil { + in, out := &in.EnvironmentRef, &out.EnvironmentRef + *out = new(v1.ObjectReference) + **out = **in + } + if in.Hardware != nil { + in, out := &in.Hardware, &out.Hardware + *out = new(HardwareInformation) + (*in).DeepCopyInto(*out) + } + if in.BMC != nil { + in, out := &in.BMC, &out.BMC + *out = new(BMC) + (*in).DeepCopyInto(*out) + } + if in.ManagementAPI != nil { + in, out := &in.ManagementAPI, &out.ManagementAPI + *out = new(ManagementAPI) + **out = **in + } + if in.ConfigPatches != nil { + in, out := &in.ConfigPatches, &out.ConfigPatches + *out = make([]ConfigPatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerSpec. +func (in *ServerSpec) DeepCopy() *ServerSpec { + if in == nil { + return nil + } + out := new(ServerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerStatus) DeepCopyInto(out *ServerStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1beta1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]v1.NodeAddress, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatus. +func (in *ServerStatus) DeepCopy() *ServerStatus { + if in == nil { + return nil + } + out := new(ServerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageDevice) DeepCopyInto(out *StorageDevice) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageDevice. +func (in *StorageDevice) DeepCopy() *StorageDevice { + if in == nil { + return nil + } + out := new(StorageDevice) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageInformation) DeepCopyInto(out *StorageInformation) { + *out = *in + if in.Devices != nil { + in, out := &in.Devices, &out.Devices + *out = make([]*StorageDevice, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StorageDevice) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageInformation. +func (in *StorageInformation) DeepCopy() *StorageInformation { + if in == nil { + return nil + } + out := new(StorageInformation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SystemInformation) DeepCopyInto(out *SystemInformation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SystemInformation. +func (in *SystemInformation) DeepCopy() *SystemInformation { + if in == nil { + return nil + } + out := new(SystemInformation) + in.DeepCopyInto(out) + return out +} diff --git a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_environments.yaml b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_environments.yaml index 1be7df478..f4885592a 100644 --- a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_environments.yaml +++ b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_environments.yaml @@ -93,6 +93,87 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: the kernel for the environment + jsonPath: .spec.kernel.url + name: Kernel + type: string + - description: the initrd for the environment + jsonPath: .spec.initrd.url + name: Initrd + type: string + - description: indicates the readiness of the environment + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - description: The age of this resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: Environment is the Schema for the environments API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: EnvironmentSpec defines the desired state of Environment. + properties: + initrd: + properties: + sha512: + type: string + url: + type: string + type: object + kernel: + properties: + args: + items: + type: string + type: array + sha512: + type: string + url: + type: string + type: object + type: object + status: + description: EnvironmentStatus defines the observed state of Environment. + properties: + conditions: + items: + properties: + sha512: + type: string + status: + type: string + type: + type: string + url: + type: string + required: + - status + - type + type: object + type: array + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_serverclasses.yaml b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_serverclasses.yaml index 3577cc945..168690bec 100644 --- a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_serverclasses.yaml +++ b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_serverclasses.yaml @@ -210,6 +210,321 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: the number of available servers + jsonPath: .status.serversAvailable + name: Available + type: string + - description: the number of servers in use + jsonPath: .status.serversInUse + name: In Use + type: string + - description: The age of this resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: ServerClass is the Schema for the serverclasses API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServerClassSpec defines the desired state of ServerClass. + properties: + bootFromDiskMethod: + description: "BootFromDiskMethod specifies the method to exit iPXE + to force boot from disk. \n If not set, controller default is used. + Valid values: ipxe-exit, http-404, ipxe-sanboot." + type: string + configPatches: + description: Set of config patches to apply to the machine configuration + to the servers provisioned via this server class. + items: + properties: + op: + type: string + path: + type: string + value: + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + environmentRef: + description: Reference to the environment which should be used to + provision the servers via this server class. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of + an entire object, this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part of + an object. TODO: this design is not final and this field is + subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + qualifiers: + description: "Qualifiers to match on the server spec. \n If qualifiers + are empty, they match all servers. Server should match both qualifiers + and selector conditions to be included into the server class." + properties: + hardware: + items: + properties: + compute: + properties: + processorCount: + format: int32 + type: integer + processors: + items: + properties: + coreCount: + format: int32 + type: integer + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + speed: + description: Speed is in megahertz (Mhz) + format: int32 + type: integer + threadCount: + format: int32 + type: integer + type: object + type: array + totalCoreCount: + format: int32 + type: integer + totalThreadCount: + format: int32 + type: integer + type: object + memory: + properties: + moduleCount: + format: int32 + type: integer + modules: + items: + properties: + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + size: + description: Size is in megabytes (MB) + format: int32 + type: integer + speed: + description: Speed is in megatransfers per second + (MT/S) + format: int32 + type: integer + type: + type: string + type: object + type: array + totalSize: + type: string + type: object + network: + properties: + interfaceCount: + format: int32 + type: integer + interfaces: + items: + properties: + addresses: + items: + type: string + type: array + flags: + type: string + index: + format: int32 + type: integer + mac: + type: string + mtu: + format: int32 + type: integer + name: + type: string + type: object + type: array + type: object + storage: + properties: + deviceCount: + format: int32 + type: integer + devices: + items: + properties: + deviceName: + type: string + name: + type: string + productName: + type: string + serialNumber: + type: string + size: + description: Size is in bytes + format: int64 + type: integer + type: + type: string + uuid: + type: string + wwid: + type: string + type: object + type: array + totalSize: + type: string + type: object + system: + properties: + family: + type: string + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + skuNumber: + type: string + uuid: + type: string + version: + type: string + type: object + type: object + type: array + labelSelectors: + items: + additionalProperties: + type: string + type: object + type: array + type: object + selector: + description: Label selector to filter the matching servers based on + labels. A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: object + status: + description: ServerClassStatus defines the observed state of ServerClass. + properties: + serversAvailable: + items: + type: string + type: array + serversInUse: + items: + type: string + type: array + required: + - serversAvailable + - serversInUse + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_servers.yaml b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_servers.yaml index df6777103..a30534044 100644 --- a/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_servers.yaml +++ b/app/sidero-controller-manager/config/crd/bases/metal.sidero.dev_servers.yaml @@ -339,6 +339,451 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: server hostname + jsonPath: .spec.hostname + name: Hostname + type: string + - description: BMC IP + jsonPath: .spec.bmc.endpoint + name: BMC IP + priority: 1 + type: string + - description: indicates if the server is accepted + jsonPath: .spec.accepted + name: Accepted + type: boolean + - description: indicates if the server is cordoned + jsonPath: .spec.cordoned + name: Cordoned + type: boolean + - description: indicates that the server has been allocated + jsonPath: .status.inUse + name: Allocated + type: boolean + - description: indicates if the server is clean or not + jsonPath: .status.isClean + name: Clean + type: boolean + - description: display the current power status + jsonPath: .status.power + name: Power + type: string + - description: The age of this resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: Server is the Schema for the servers API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServerSpec defines the desired state of Server. + properties: + accepted: + type: boolean + bmc: + description: BMC defines data about how to talk to the node via ipmitool. + properties: + endpoint: + description: BMC endpoint. + type: string + interface: + description: BMC Interface Type. Defaults to lanplus. + type: string + pass: + description: BMC password value. + type: string + passFrom: + description: Source for the password value. Cannot be used if + Pass is not empty. + properties: + secretKeyRef: + description: SecretKeyRef defines a ref to a given key within + a secret. + properties: + key: + description: Key to select + type: string + name: + type: string + namespace: + description: 'Namespace and name of credential secret + nb: can''t use namespacedname here b/c it doesn''t have + json tags in the struct :(' + type: string + required: + - key + - name + - namespace + type: object + type: object + port: + description: BMC port. Defaults to 623. + format: int32 + type: integer + user: + description: BMC user value. + type: string + userFrom: + description: Source for the user value. Cannot be used if User + is not empty. + properties: + secretKeyRef: + description: SecretKeyRef defines a ref to a given key within + a secret. + properties: + key: + description: Key to select + type: string + name: + type: string + namespace: + description: 'Namespace and name of credential secret + nb: can''t use namespacedname here b/c it doesn''t have + json tags in the struct :(' + type: string + required: + - key + - name + - namespace + type: object + type: object + required: + - endpoint + type: object + bootFromDiskMethod: + description: "BootFromDiskMethod specifies the method to exit iPXE + to force boot from disk. \n If not set, controller default is used. + Valid values: ipxe-exit, http-404, ipxe-sanboot." + type: string + configPatches: + items: + properties: + op: + type: string + path: + type: string + value: + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + cordoned: + type: boolean + environmentRef: + description: 'ObjectReference contains enough information to let you + inspect or modify the referred object. --- New uses of this type + are discouraged because of difficulty describing its usage when + embedded in APIs. 1. Ignored fields. It includes many fields which + are not generally honored. For instance, ResourceVersion and FieldPath + are both very rarely valid in actual usage. 2. Invalid usage help. It + is impossible to add specific help for individual usage. In most + embedded usages, there are particular restrictions like, "must refer + only to types A and B" or "UID not honored" or "name must be restricted". + Those cannot be well described when embedded. 3. Inconsistent validation. Because + the usages are different, the validation rules are different by + usage, which makes it hard for users to predict what will happen. + 4. The fields are both imprecise and overly precise. Kind is not + a precise mapping to a URL. This can produce ambiguity during interpretation + and require a REST mapping. In most cases, the dependency is on + the group,resource tuple and the version of the actual struct is + irrelevant. 5. We cannot easily change it. Because this type is + embedded in many locations, updates to this type will affect numerous + schemas. Don''t make new APIs embed an underspecified API type + they do not control. Instead of using this type, create a locally + provided and used type that is well-focused on your reference. For + example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 + .' + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of + an entire object, this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part of + an object. TODO: this design is not final and this field is + subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + hardware: + properties: + compute: + properties: + processorCount: + format: int32 + type: integer + processors: + items: + properties: + coreCount: + format: int32 + type: integer + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + speed: + description: Speed is in megahertz (Mhz) + format: int32 + type: integer + threadCount: + format: int32 + type: integer + type: object + type: array + totalCoreCount: + format: int32 + type: integer + totalThreadCount: + format: int32 + type: integer + type: object + memory: + properties: + moduleCount: + format: int32 + type: integer + modules: + items: + properties: + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + size: + description: Size is in megabytes (MB) + format: int32 + type: integer + speed: + description: Speed is in megatransfers per second (MT/S) + format: int32 + type: integer + type: + type: string + type: object + type: array + totalSize: + type: string + type: object + network: + properties: + interfaceCount: + format: int32 + type: integer + interfaces: + items: + properties: + addresses: + items: + type: string + type: array + flags: + type: string + index: + format: int32 + type: integer + mac: + type: string + mtu: + format: int32 + type: integer + name: + type: string + type: object + type: array + type: object + storage: + properties: + deviceCount: + format: int32 + type: integer + devices: + items: + properties: + deviceName: + type: string + name: + type: string + productName: + type: string + serialNumber: + type: string + size: + description: Size is in bytes + format: int64 + type: integer + type: + type: string + uuid: + type: string + wwid: + type: string + type: object + type: array + totalSize: + type: string + type: object + system: + properties: + family: + type: string + manufacturer: + type: string + productName: + type: string + serialNumber: + type: string + skuNumber: + type: string + uuid: + type: string + version: + type: string + type: object + type: object + hostname: + type: string + managementApi: + description: ManagementAPI defines data about how to talk to the node + via simple HTTP API. + properties: + endpoint: + type: string + required: + - endpoint + type: object + pxeBootAlways: + type: boolean + required: + - accepted + type: object + status: + description: ServerStatus defines the observed state of Server. + properties: + addresses: + description: Addresses lists discovered node IPs. + items: + description: NodeAddress contains information for the node's address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + type: array + conditions: + description: Conditions defines current service state of the Server. + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. This field may be empty. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. The specific API may choose whether or not this + field is considered a guaranteed API. This field may not be + empty. + type: string + severity: + description: Severity provides an explicit classification of + Reason code, so the users or machines can immediately understand + the current situation and act accordingly. The Severity field + MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + inUse: + description: InUse is true when server is assigned to some MetalMachine. + type: boolean + isClean: + description: IsClean is true when server disks are wiped. + type: boolean + power: + description: 'Power is the current power state of the server: "on", + "off" or "unknown".' + type: string + ready: + description: Ready is true when server is accepted and in use. + type: boolean + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/app/sidero-controller-manager/config/crd/patches/webhook_in_environments.yaml b/app/sidero-controller-manager/config/crd/patches/webhook_in_environments.yaml index b66be96c2..c544e3afa 100644 --- a/app/sidero-controller-manager/config/crd/patches/webhook_in_environments.yaml +++ b/app/sidero-controller-manager/config/crd/patches/webhook_in_environments.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: sidero-webhook-service path: /convert diff --git a/app/sidero-controller-manager/config/crd/patches/webhook_in_serverclasses.yaml b/app/sidero-controller-manager/config/crd/patches/webhook_in_serverclasses.yaml index 73196078f..b6d7513a4 100644 --- a/app/sidero-controller-manager/config/crd/patches/webhook_in_serverclasses.yaml +++ b/app/sidero-controller-manager/config/crd/patches/webhook_in_serverclasses.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: sidero-webhook-service path: /convert diff --git a/app/sidero-controller-manager/config/crd/patches/webhook_in_servers.yaml b/app/sidero-controller-manager/config/crd/patches/webhook_in_servers.yaml index 99a9aa48e..76cc70a71 100644 --- a/app/sidero-controller-manager/config/crd/patches/webhook_in_servers.yaml +++ b/app/sidero-controller-manager/config/crd/patches/webhook_in_servers.yaml @@ -15,5 +15,5 @@ spec: caBundle: Cg== service: namespace: system - name: webhook-service + name: sidero-webhook-service path: /convert diff --git a/app/sidero-controller-manager/config/webhook/manifests.yaml b/app/sidero-controller-manager/config/webhook/manifests.yaml index 763cf66d6..a87b8ae64 100644 --- a/app/sidero-controller-manager/config/webhook/manifests.yaml +++ b/app/sidero-controller-manager/config/webhook/manifests.yaml @@ -5,6 +5,27 @@ metadata: creationTimestamp: null name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-metal-sidero-dev-v1alpha2-server + failurePolicy: Fail + name: vservers.metal.sidero.dev + rules: + - apiGroups: + - metal.sidero.dev + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - servers + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/app/sidero-controller-manager/main.go b/app/sidero-controller-manager/main.go index 99258fd57..010851c94 100644 --- a/app/sidero-controller-manager/main.go +++ b/app/sidero-controller-manager/main.go @@ -25,13 +25,14 @@ import ( "k8s.io/client-go/tools/record" capi "sigs.k8s.io/cluster-api/api/v1beta1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" infrav1alpha3 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3" - "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1" metalv1alpha1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1" + metalv1alpha2 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha2" "github.com/talos-systems/sidero/app/sidero-controller-manager/controllers" "github.com/talos-systems/sidero/app/sidero-controller-manager/internal/ipxe" "github.com/talos-systems/sidero/app/sidero-controller-manager/internal/metadata" @@ -42,7 +43,6 @@ import ( "github.com/talos-systems/sidero/app/sidero-controller-manager/internal/tftp" "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/constants" siderotypes "github.com/talos-systems/sidero/app/sidero-controller-manager/pkg/types" - "github.com/talos-systems/sidero/internal/client" // +kubebuilder:scaffold:imports ) @@ -65,6 +65,7 @@ func init() { _ = capi.AddToScheme(scheme) _ = metalv1alpha1.AddToScheme(scheme) + _ = metalv1alpha2.AddToScheme(scheme) _ = infrav1alpha3.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } @@ -258,26 +259,22 @@ func main() { grpcServer := server.CreateServer(mgr.GetClient(), apiRecorder, mgr.GetScheme(), autoAcceptServers, insecureWipe, autoBMCSetup, serverRebootTimeout) - k8sClient, err := client.NewClient(nil) - if err != nil { - setupLog.Error(err, `failed to create k8s client`) - os.Exit(1) - } - - if err = controllers.ReconcileServerClassAny(ctx, k8sClient); err != nil { - setupLog.Error(err, `failed to reconcile ServerClass "any"`) + if err = mgr.Add(manager.RunnableFunc(func(ctx context.Context) error { + return siderolink.Cfg.LoadOrCreate(ctx, mgr.GetClient()) + })); err != nil { + setupLog.Error(err, `failed to add SideroLink configuration initialization`) os.Exit(1) } - if err = controllers.ReconcileEnvironmentDefault(ctx, k8sClient, TalosRelease, apiEndpoint, uint16(apiPort)); err != nil { - setupLog.Error(err, `failed to reconcile Environment "default"`) + if err = mgr.Add(RunnableClientFunc(controllers.ReconcileServerClassAny)); err != nil { + setupLog.Error(err, `failed to add initial reconcile`) os.Exit(1) } - if err = mgr.Add(manager.RunnableFunc(func(ctx context.Context) error { - return siderolink.Cfg.LoadOrCreate(ctx, mgr.GetClient()) + if err = mgr.Add(RunnableClientFunc(func(ctx context.Context, k8sClient client.Client) error { + return controllers.ReconcileEnvironmentDefault(ctx, k8sClient, TalosRelease, apiEndpoint, uint16(apiPort)) })); err != nil { - setupLog.Error(err, `failed to add SideroLink configuration initialization`) + setupLog.Error(err, `failed to add initial reconcile`) os.Exit(1) } @@ -329,17 +326,32 @@ func main() { } func setupWebhooks(mgr ctrl.Manager) { - if err := (&v1alpha1.ServerClass{}).SetupWebhookWithManager(mgr); err != nil { + if err := (&metalv1alpha1.ServerClass{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "ServerClass") os.Exit(1) } - if err := (&v1alpha1.Environment{}).SetupWebhookWithManager(mgr); err != nil { + if err := (&metalv1alpha1.Environment{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Environment") os.Exit(1) } - if err := (&v1alpha1.Server{}).SetupWebhookWithManager(mgr); err != nil { + if err := (&metalv1alpha1.Server{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Server") + os.Exit(1) + } + + if err := (&metalv1alpha2.ServerClass{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ServerClass") + os.Exit(1) + } + + if err := (&metalv1alpha2.Environment{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Environment") + os.Exit(1) + } + + if err := (&metalv1alpha2.Server{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Server") os.Exit(1) } @@ -358,3 +370,29 @@ func setupChecks(mgr ctrl.Manager, httpPort int) { os.Exit(1) } } + +// RunnableClientFunc implements Runnable and inject.Client using a function. +func RunnableClientFunc(f func(context.Context, client.Client) error) *runnableClientFunc { + return &runnableClientFunc{ + Func: f, + } +} + +type runnableClientFunc struct { + Client client.Client + Func func(context.Context, client.Client) error +} + +// InjectClient implements inject.Client. +// +//nolint:unparam +func (r *runnableClientFunc) InjectClient(c client.Client) error { + r.Client = c + + return nil +} + +// Start implements Runnable. +func (r *runnableClientFunc) Start(ctx context.Context) error { + return r.Func(ctx, r.Client) +} diff --git a/sfyra/pkg/tests/compatibility.go b/sfyra/pkg/tests/compatibility.go index 532cb7807..bc7fb8764 100644 --- a/sfyra/pkg/tests/compatibility.go +++ b/sfyra/pkg/tests/compatibility.go @@ -59,10 +59,10 @@ func TestCompatibilityCluster(ctx context.Context, metalClient client.Client, cl environment.APIVersion = constants.SideroAPIVersion environment.Name = envName - environment.Spec.Kernel.URL = fmt.Sprintf("https://github.com/talos-systems/talos/releases/download/%s/vmlinuz-amd64", talosRelease) + environment.Spec.Kernel.URL = fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/vmlinuz-amd64", talosRelease) environment.Spec.Kernel.SHA512 = "" environment.Spec.Kernel.Args = cmdline.Strings() - environment.Spec.Initrd.URL = fmt.Sprintf("https://github.com/talos-systems/talos/releases/download/%s/initramfs-amd64.xz", talosRelease) + environment.Spec.Initrd.URL = fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/initramfs-amd64.xz", talosRelease) environment.Spec.Initrd.SHA512 = "" require.NoError(t, metalClient.Create(ctx, &environment))