diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md index c0ab137..f1fe852 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -381,6 +381,20 @@ by extra ignition.

+ + +networks
+ + +[]Networks + + + + +(Optional) +

Networks is the metal specific network configuration.

+ +

InfrastructureStatus @@ -698,6 +712,59 @@ bool +

Networks +

+

+(Appears on: +InfrastructureConfig) +

+

+

Networks holds information about the Kubernetes and infrastructure networks.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

Name is the name for this CIDR.

+
+cidr
+ +string + +
+

CIDR is the workers subnet range to create.

+
+id
+ +string + +
+(Optional) +

ID is the ID for the workers’ subnet.

+

RegionConfig

diff --git a/pkg/apis/metal/types_infrastructure.go b/pkg/apis/metal/types_infrastructure.go index b19e1ac..cd82e65 100644 --- a/pkg/apis/metal/types_infrastructure.go +++ b/pkg/apis/metal/types_infrastructure.go @@ -13,6 +13,9 @@ import ( // InfrastructureConfig infrastructure configuration resource type InfrastructureConfig struct { metav1.TypeMeta + + // Networks is the metal specific network configuration. + Networks []Networks } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -21,3 +24,13 @@ type InfrastructureConfig struct { type InfrastructureStatus struct { metav1.TypeMeta } + +// Networks holds information about the Kubernetes and infrastructure networks. +type Networks struct { + // Name is the name for this network. + Name string + // CIDR is the workers subnet range to create. + CIDR string + // ID is the ID for the workers' subnet. + ID string +} diff --git a/pkg/apis/metal/v1alpha1/types_infrastructure.go b/pkg/apis/metal/v1alpha1/types_infrastructure.go index 65f87fb..52905b3 100644 --- a/pkg/apis/metal/v1alpha1/types_infrastructure.go +++ b/pkg/apis/metal/v1alpha1/types_infrastructure.go @@ -14,6 +14,10 @@ import ( // InfrastructureConfig infrastructure configuration resource type InfrastructureConfig struct { metav1.TypeMeta `json:",inline"` + + // Networks is the metal specific network configuration. + // +optional + Networks []Networks `json:"networks,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -22,3 +26,14 @@ type InfrastructureConfig struct { type InfrastructureStatus struct { metav1.TypeMeta `json:",inline"` } + +// Networks holds information about the Kubernetes and infrastructure networks. +type Networks struct { + // Name is the name for this CIDR. + Name string `json:"name"` + // CIDR is the workers subnet range to create. + CIDR string `json:"cidr"` + // ID is the ID for the workers' subnet. + // +optional + ID string `json:"id,omitempty"` +} diff --git a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go index 32bb196..5c52a49 100644 --- a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go @@ -164,6 +164,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*Networks)(nil), (*metal.Networks)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Networks_To_metal_Networks(a.(*Networks), b.(*metal.Networks), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*metal.Networks)(nil), (*Networks)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_metal_Networks_To_v1alpha1_Networks(a.(*metal.Networks), b.(*Networks), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*RegionConfig)(nil), (*metal.RegionConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_RegionConfig_To_metal_RegionConfig(a.(*RegionConfig), b.(*metal.RegionConfig), scope) }); err != nil { @@ -340,6 +350,7 @@ func Convert_metal_IgnitionConfig_To_v1alpha1_IgnitionConfig(in *metal.IgnitionC } func autoConvert_v1alpha1_InfrastructureConfig_To_metal_InfrastructureConfig(in *InfrastructureConfig, out *metal.InfrastructureConfig, s conversion.Scope) error { + out.Networks = *(*[]metal.Networks)(unsafe.Pointer(&in.Networks)) return nil } @@ -349,6 +360,7 @@ func Convert_v1alpha1_InfrastructureConfig_To_metal_InfrastructureConfig(in *Inf } func autoConvert_metal_InfrastructureConfig_To_v1alpha1_InfrastructureConfig(in *metal.InfrastructureConfig, out *InfrastructureConfig, s conversion.Scope) error { + out.Networks = *(*[]Networks)(unsafe.Pointer(&in.Networks)) return nil } @@ -515,6 +527,30 @@ func Convert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(in *metal.MetallbConf return autoConvert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(in, out, s) } +func autoConvert_v1alpha1_Networks_To_metal_Networks(in *Networks, out *metal.Networks, s conversion.Scope) error { + out.Name = in.Name + out.CIDR = in.CIDR + out.ID = in.ID + return nil +} + +// Convert_v1alpha1_Networks_To_metal_Networks is an autogenerated conversion function. +func Convert_v1alpha1_Networks_To_metal_Networks(in *Networks, out *metal.Networks, s conversion.Scope) error { + return autoConvert_v1alpha1_Networks_To_metal_Networks(in, out, s) +} + +func autoConvert_metal_Networks_To_v1alpha1_Networks(in *metal.Networks, out *Networks, s conversion.Scope) error { + out.Name = in.Name + out.CIDR = in.CIDR + out.ID = in.ID + return nil +} + +// Convert_metal_Networks_To_v1alpha1_Networks is an autogenerated conversion function. +func Convert_metal_Networks_To_v1alpha1_Networks(in *metal.Networks, out *Networks, s conversion.Scope) error { + return autoConvert_metal_Networks_To_v1alpha1_Networks(in, out, s) +} + func autoConvert_v1alpha1_RegionConfig_To_metal_RegionConfig(in *RegionConfig, out *metal.RegionConfig, s conversion.Scope) error { out.Name = in.Name out.Server = in.Server diff --git a/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go index 520f4cd..857e07d 100644 --- a/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go @@ -194,6 +194,11 @@ func (in *IgnitionConfig) DeepCopy() *IgnitionConfig { func (in *InfrastructureConfig) DeepCopyInto(out *InfrastructureConfig) { *out = *in out.TypeMeta = in.TypeMeta + if in.Networks != nil { + in, out := &in.Networks, &out.Networks + *out = make([]Networks, len(*in)) + copy(*out, *in) + } return } @@ -375,6 +380,22 @@ func (in *MetallbConfig) DeepCopy() *MetallbConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Networks) DeepCopyInto(out *Networks) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networks. +func (in *Networks) DeepCopy() *Networks { + if in == nil { + return nil + } + out := new(Networks) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegionConfig) DeepCopyInto(out *RegionConfig) { *out = *in diff --git a/pkg/apis/metal/zz_generated.deepcopy.go b/pkg/apis/metal/zz_generated.deepcopy.go index f79938b..b82220e 100644 --- a/pkg/apis/metal/zz_generated.deepcopy.go +++ b/pkg/apis/metal/zz_generated.deepcopy.go @@ -194,6 +194,11 @@ func (in *IgnitionConfig) DeepCopy() *IgnitionConfig { func (in *InfrastructureConfig) DeepCopyInto(out *InfrastructureConfig) { *out = *in out.TypeMeta = in.TypeMeta + if in.Networks != nil { + in, out := &in.Networks, &out.Networks + *out = make([]Networks, len(*in)) + copy(*out, *in) + } return } @@ -375,6 +380,22 @@ func (in *MetallbConfig) DeepCopy() *MetallbConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Networks) DeepCopyInto(out *Networks) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networks. +func (in *Networks) DeepCopy() *Networks { + if in == nil { + return nil + } + out := new(Networks) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegionConfig) DeepCopyInto(out *RegionConfig) { *out = *in diff --git a/pkg/controller/infrastructure/actuator_reconcile.go b/pkg/controller/infrastructure/actuator_reconcile.go index 762398f..f4f8465 100644 --- a/pkg/controller/infrastructure/actuator_reconcile.go +++ b/pkg/controller/infrastructure/actuator_reconcile.go @@ -5,17 +5,56 @@ package infrastructure import ( "context" + "encoding/json" + "fmt" "github.com/gardener/gardener/extensions/pkg/controller" extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" "github.com/go-logr/logr" + + metalv1alpha1 "github.com/ironcore-dev/gardener-extension-provider-metal/pkg/apis/metal/v1alpha1" +) + +var ( + infrastructureConfig metalv1alpha1.InfrastructureConfig ) -// Reconcile implements infrastructure.Actuator. +// Reconcile implements infrastructure actuator reconciliation func (a *actuator) Reconcile(ctx context.Context, log logr.Logger, infra *extensionsv1alpha1.Infrastructure, cluster *controller.Cluster) error { + err := json.Unmarshal(cluster.Shoot.Spec.Provider.InfrastructureConfig.Raw, &infrastructureConfig) + if err != nil { + return fmt.Errorf("failed to unmarshal infrastructure config: %w", err) + } + + var newNodes []string + if infrastructureConfig.Networks != nil { + for _, network := range infrastructureConfig.Networks { + if network.Name == "" { + return fmt.Errorf("network name is required") + } + newNodes = append(newNodes, network.CIDR) + } + } + + if !equalStringSlices(infra.Status.Networking.Nodes, newNodes) { + infra.Status.Networking.Nodes = newNodes + } + return a.reconcile(ctx, log, infra, cluster) } +func equalStringSlices(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} + func (a *actuator) reconcile(ctx context.Context, log logr.Logger, infra *extensionsv1alpha1.Infrastructure, cluster *controller.Cluster) error { return nil } diff --git a/pkg/controller/infrastructure/actuator_reconcile_test.go b/pkg/controller/infrastructure/actuator_reconcile_test.go new file mode 100644 index 0000000..7115357 --- /dev/null +++ b/pkg/controller/infrastructure/actuator_reconcile_test.go @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package infrastructure + +import ( + "context" + "encoding/json" + + extensionscontroller "github.com/gardener/gardener/extensions/pkg/controller" + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" + "github.com/go-logr/logr" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" + + metalv1alpha1 "github.com/ironcore-dev/gardener-extension-provider-metal/pkg/apis/metal/v1alpha1" +) + +var ( + shootVersionMajorMinor = "1.2" + shootVersion = shootVersionMajorMinor + ".3" +) + +var _ = Describe("Actuator Reconcile", func() { + var ( + ctx context.Context + log logr.Logger + infra *extensionsv1alpha1.Infrastructure + cluster *extensionscontroller.Cluster + act *actuator + ) + + BeforeEach(func() { + ctx = context.TODO() + log = logr.Discard() + + infra = &extensionsv1alpha1.Infrastructure{ + Status: extensionsv1alpha1.InfrastructureStatus{ + Networking: &extensionsv1alpha1.InfrastructureStatusNetworking{ + Nodes: []string{}, + }, + }, + } + + infrastructureConfig := metalv1alpha1.InfrastructureConfig{ + Networks: []metalv1alpha1.Networks{ + {Name: "worker-network-1", CIDR: "10.10.10.0/24", ID: "1"}, + {Name: "worker-network-2", CIDR: "10.10.20.0/24", ID: "2"}, + }, + } + infrastructureConfigRaw, _ := json.Marshal(infrastructureConfig) + + cluster = &extensionscontroller.Cluster{ + Shoot: &gardencorev1beta1.Shoot{ + Spec: gardencorev1beta1.ShootSpec{ + Kubernetes: gardencorev1beta1.Kubernetes{ + Version: shootVersion, + }, + Provider: gardencorev1beta1.Provider{ + InfrastructureConfig: &runtime.RawExtension{ + Raw: infrastructureConfigRaw, + }, + }, + }, + }, + } + + act = &actuator{} + }) + + Describe("#Reconcile", func() { + It("should update infra.Status.Networking.Nodes", func() { + err := act.Reconcile(ctx, log, infra, cluster) + Expect(err).NotTo(HaveOccurred()) + + // Verify that infra.Status.Networking.Nodes is updated + expectedNodes := []string{"10.10.10.0/24", "10.10.20.0/24"} + Expect(infra.Status.Networking.Nodes).To(Equal(expectedNodes)) + }) + }) +}) diff --git a/pkg/controller/infrastructure/infrastructure_suite_test.go b/pkg/controller/infrastructure/infrastructure_suite_test.go new file mode 100644 index 0000000..de58cd1 --- /dev/null +++ b/pkg/controller/infrastructure/infrastructure_suite_test.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package infrastructure_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestInfrastructure(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Infrastructure Suite") +}