Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add IPAM for nodes #142

Merged
merged 16 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions api/v1alpha1/ionoscloudmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,12 @@ type IonosCloudMachineSpec struct {
Disk *Volume `json:"disk"`

// AdditionalNetworks defines the additional network configurations for the VM.
//
//+optional
AdditionalNetworks Networks `json:"additionalNetworks,omitempty"`
AdditionalNetworks []Network `json:"additionalNetworks,omitempty"`

// IPAMConfig allows to obtain IP Addresses from existing IP pools instead of using DHCP.
IPAMConfig `json:",inline"`

// FailoverIP can be set to enable failover for VMs in the same MachineDeployment.
// It can be either set to an already reserved IPv4 address, or it can be set to "AUTO"
Expand All @@ -172,10 +176,6 @@ type IonosCloudMachineSpec struct {
Type ServerType `json:"type,omitempty"`
}

// Networks contains a list of additional LAN IDs
// that should be attached to the VM.
type Networks []Network

// Network contains the config for additional LANs.
type Network struct {
// NetworkID represents an ID an existing LAN in the data center.
Expand All @@ -192,6 +192,9 @@ type Network struct {
//+kubebuilder:default=true
//+optional
DHCP *bool `json:"dhcp,omitempty"`

// IPAMConfig allows to obtain IP Addresses from existing IP pools instead of using DHCP.
IPAMConfig `json:",inline"`
}

// Volume is the physical storage on the VM.
Expand Down Expand Up @@ -259,7 +262,7 @@ type IonosCloudMachineStatus struct {
Ready bool `json:"ready"`

// MachineNetworkInfo contains information about the network configuration of the VM.
// This information is only available after the VM has been provisioned.
//+optional
MachineNetworkInfo *MachineNetworkInfo `json:"machineNetworkInfo,omitempty"`

// FailureReason will be set in the event that there is a terminal problem
Expand Down Expand Up @@ -315,6 +318,8 @@ type IonosCloudMachineStatus struct {
}

// MachineNetworkInfo contains information about the network configuration of the VM.
// Before the provisioning MachineNetworkInfo may contain IP addresses to be used for provisioning.
// After provisioning this information is available completely.
type MachineNetworkInfo struct {
// NICInfo holds information about the NICs, which are attached to the VM.
//+optional
Expand All @@ -324,10 +329,16 @@ type MachineNetworkInfo struct {
// NICInfo provides information about the NIC of the VM.
type NICInfo struct {
// IPv4Addresses contains the IPv4 addresses of the NIC.
IPv4Addresses []string `json:"ipv4Addresses"`
// By default, we enable dual-stack, but as we are storing the IP obtained from AddressClaims here before
// creating the VM this can be temporarily empty, e.g. we use DHCP for IPv4 and fixed IP for IPv6.
//+optional
IPv4Addresses []string `json:"ipv4Addresses,omitempty"`

// IPv6Addresses contains the IPv6 addresses of the NIC.
IPv6Addresses []string `json:"ipv6Addresses"`
// By default, we enable dual-stack, but as we are storing the IP obtained from AddressClaims here before
// creating the VM this can be temporarily empty, e.g. we use DHCP for IPv6 and fixed IP for IPv4.
//+optional
IPv6Addresses []string `json:"ipv6Addresses,omitempty"`

// NetworkID is the ID of the LAN to which the NIC is connected.
NetworkID int32 `json:"networkID"`
Expand Down
53 changes: 52 additions & 1 deletion api/v1alpha1/ionoscloudmachine_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func defaultMachine() *IonosCloudMachine {
ID: "1eef-48ec-a246-a51a33aa4f3a",
},
},
AdditionalNetworks: Networks{
AdditionalNetworks: []Network{
{
NetworkID: 1,
},
Expand All @@ -64,6 +64,20 @@ func defaultMachine() *IonosCloudMachine {
}
}

func setInvalidPoolRef(m *IonosCloudMachine, poolType string, kind, apiGroup, name string) {
ref := &corev1.TypedLocalObjectReference{
APIGroup: ptr.To(apiGroup),
Kind: kind,
Name: name,
}
switch poolType {
case "IPv6":
m.Spec.AdditionalNetworks[0].IPv6PoolRef = ref
case "IPv4":
m.Spec.AdditionalNetworks[0].IPv4PoolRef = ref
}
}

var _ = Describe("IonosCloudMachine Tests", func() {
AfterEach(func() {
m := &IonosCloudMachine{
Expand Down Expand Up @@ -354,6 +368,43 @@ var _ = Describe("IonosCloudMachine Tests", func() {
m.Spec.AdditionalNetworks[0].NetworkID = -1
Expect(k8sClient.Create(context.Background(), m)).ToNot(Succeed())
})
DescribeTable("should allow IPv4PoolRef.Kind GlobalInClusterIPPool and InClusterIPPool", func(kind string) {
m := defaultMachine()
m.Spec.AdditionalNetworks[0].IPv4PoolRef = &corev1.TypedLocalObjectReference{
APIGroup: ptr.To("ipam.cluster.x-k8s.io"),
Kind: kind,
Name: "ipv4-pool",
}
Expect(k8sClient.Create(context.Background(), m)).To(Succeed())
},
Entry("GlobalInClusterIPPool", "GlobalInClusterIPPool"),
Entry("InClusterIPPool", "InClusterIPPool"),
)
DescribeTable("should allow IPv6PoolRef.Kind GlobalInClusterIPPool and InClusterIPPool", func(kind string) {
m := defaultMachine()
m.Spec.AdditionalNetworks[0].IPv6PoolRef = &corev1.TypedLocalObjectReference{
APIGroup: ptr.To("ipam.cluster.x-k8s.io"),
Kind: kind,
Name: "ipv6-pool",
}
Expect(k8sClient.Create(context.Background(), m)).To(Succeed())
},
Entry("GlobalInClusterIPPool", "GlobalInClusterIPPool"),
Entry("InClusterIPPool", "InClusterIPPool"),
)
DescribeTable("must not allow invalid pool references",
func(poolType, kind, apiGroup, name string) {
m := defaultMachine()
setInvalidPoolRef(m, poolType, kind, apiGroup, name)
Expect(k8sClient.Create(context.Background(), m)).ToNot(Succeed())
},
Entry("invalid IPv6PoolRef with invalid kind", "IPv6", "SomeOtherIPPoolKind", "ipam.cluster.x-k8s.io", "ipv6-pool"),
Entry("invalid IPv6PoolRef with invalid apiGroup", "IPv6", "InClusterIPPool", "SomeWrongAPIGroup", "ipv6-pool"),
Entry("invalid IPv6PoolRef with empty name", "IPv6", "InClusterIPPool", "ipam.cluster.x-k8s.io", ""),
Entry("invalid IPv4PoolRef with invalid kind", "IPv4", "SomeOtherIPPoolKind", "ipam.cluster.x-k8s.io", "ipv4-pool"),
Entry("invalid IPv4PoolRef with invalid apiGroup", "IPv4", "InClusterIPPool", "SomeWrongAPIGroup", "ipv4-pool"),
Entry("invalid IPv4PoolRef with empty name", "IPv4", "InClusterIPPool", "ipam.cluster.x-k8s.io", ""),
)
It("DHCP should default to true", func() {
m := defaultMachine()
Expect(k8sClient.Create(context.Background(), m)).To(Succeed())
Expand Down
2 changes: 2 additions & 0 deletions api/v1alpha1/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"testing"

"k8s.io/apimachinery/pkg/runtime"
ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
Expand Down Expand Up @@ -53,6 +54,7 @@ var _ = BeforeSuite(func() {

scheme := runtime.NewScheme()
Expect(AddToScheme(scheme)).To(Succeed())
Expect(ipamv1.AddToScheme(scheme)).To(Succeed())

cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expand Down
21 changes: 21 additions & 0 deletions api/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ limitations under the License.

package v1alpha1

import corev1 "k8s.io/api/core/v1"

// ProvisioningRequest is a definition of a provisioning request
// in the IONOS Cloud.
type ProvisioningRequest struct {
Expand All @@ -30,3 +32,22 @@ type ProvisioningRequest struct {
//+optional
State string `json:"state,omitempty"`
}

// IPAMConfig optionally defines which IP Pools to use.
type IPAMConfig struct {
// IPv4PoolRef is a reference to an IPAMConfig Pool resource, which exposes IPv4 addresses.
// The NIC will use an available IP address from the referenced pool.
// +kubebuilder:validation:XValidation:rule="self.apiGroup == 'ipam.cluster.x-k8s.io'",message="ipv4PoolRef allows only IPAMConfig apiGroup ipam.cluster.x-k8s.io"
// +kubebuilder:validation:XValidation:rule="self.kind == 'InClusterIPPool' || self.kind == 'GlobalInClusterIPPool'",message="ipv4PoolRef allows either InClusterIPPool or GlobalInClusterIPPool"
// +kubebuilder:validation:XValidation:rule="self.name != ''",message="ipv4PoolRef.name is required"
// +optional
IPv4PoolRef *corev1.TypedLocalObjectReference `json:"ipv4PoolRef,omitempty"`

// IPv6PoolRef is a reference to an IPAMConfig pool resource, which exposes IPv6 addresses.
// The NIC will use an available IP address from the referenced pool.
// +kubebuilder:validation:XValidation:rule="self.apiGroup == 'ipam.cluster.x-k8s.io'",message="ipv6PoolRef allows only IPAMConfig apiGroup ipam.cluster.x-k8s.io"
// +kubebuilder:validation:XValidation:rule="self.kind == 'InClusterIPPool' || self.kind == 'GlobalInClusterIPPool'",message="ipv6PoolRef allows either InClusterIPPool or GlobalInClusterIPPool"
// +kubebuilder:validation:XValidation:rule="self.name != ''",message="ipv6PoolRef.name is required"
// +optional
IPv6PoolRef *corev1.TypedLocalObjectReference `json:"ipv6PoolRef,omitempty"`
}
50 changes: 28 additions & 22 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
"sigs.k8s.io/cluster-api/util/flags"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/controller"
Expand All @@ -53,6 +54,8 @@ func init() {

utilruntime.Must(clusterv1.AddToScheme(scheme))
utilruntime.Must(infrav1.AddToScheme(scheme))
utilruntime.Must(ipamv1.AddToScheme(scheme))

//+kubebuilder:scaffold:scheme
}

Expand Down
Loading
Loading