Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ resources:
kind: DHCPRelay
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
domain: networking.metal.ironcore.dev
group: core

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
group: core

not correct.

kind: StaticRoute
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
Expand Down
13 changes: 13 additions & 0 deletions api/core/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const L2VNILabel = "networking.metal.ironcore.dev/evi-name"
// the name of the VRF they belong to.
const VRFLabel = "networking.metal.ironcore.dev/vrf-name"

// StaticRouteLabel is a label applied to VRFs to indicate
// the name of the StaticRoute that references them.
const StaticRouteLabel = "networking.metal.ironcore.dev/static-route-name"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will this work when there are multiple StaticRoutes in a VRF? Based on the way we structure the StaticRoute CRD this would be possible, no?


// DeviceMaintenanceAnnotation is an annotation that can be applied to Device objects
// to trigger certain disruptive operations, such as reboots or firmware upgrades.
const DeviceMaintenanceAnnotation = "networking.metal.ironcore.dev/maintenance"
Expand Down Expand Up @@ -217,6 +221,15 @@ const (
// VRFNotFoundReason indicates that a referenced VRF was not found.
VRFNotFoundReason = "VRFNotFound"

// VRFNotConfiguredReason indicates that a referenced VRF is not configured.
VRFNotConfiguredReason = "VRFNotConfigured"

// VRFAlreadyInUseReason indicates that a referenced VRF is already in use by another interface or static route.
VRFAlreadyInUseReason = "VRFAlreadyInUse"

// InterfaceNotConfiguredReason indicates that a referenced interface is not configured.
InterfaceNotConfiguredReason = "InterfaceNotConfigured"

// ParentInterfaceNotFoundReason indicates that a referenced parent interface for a subinterface was not found.
ParentInterfaceNotFoundReason = "ParentInterfaceNotFound"

Expand Down
145 changes: 145 additions & 0 deletions api/core/v1alpha1/staticroute_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong Year. Also applies to other newly created files.

// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
"sync"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// StaticRouteSpec defines the desired state of StaticRoute
// Static routes are used to define explicit paths for network traffic. They can be categorized into different types based on their characteristics and use cases:
// Directly Connected Routes: Only output interfaces are specified, and the next hop is directly reachable through those interfaces.
// Recursive Static Routes: In a recursive static route, only the next hop is specified. The output interface is derived from the next hop.
// Fully Specified Static Routes: Specifies both the output interfaces and the next hop, providing a complete path for the traffic.
// Floating Static Routes: These routes have a higher administrative distance than dynamic routes, allowing them to serve as backup routes that are only used when the primary route is unavailable.
type StaticRouteSpec struct {
// DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace.
// Immutable.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DeviceRef is immutable"
DeviceRef LocalObjectReference `json:"deviceRef"`

// ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this interface.
// This reference is used to link the Interface to its provider-specific configuration.
// +optional
ProviderConfigRef *TypedLocalObjectReference `json:"providerConfigRef,omitempty"`

// Name is the name of the static route.
// +required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=255
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Name is immutable"
Name string `json:"name"`

// +optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have a very brief comment, as all other fields.

// +kubebuilder:validation:MaxLength=255
Description string `json:"description,omitempty"`

// VrfRef is a reference to the VRF resource that this static route belongs to.
// If not specified, the static route will be part of the default VRF.
// The referenced VRF must exist in the same namespace.
// +optional
VrfRef *LocalObjectReference `json:"vrfRef,omitempty"`

// IPPrefix is the destination IP prefix for the static route.
// +required
Prefix IPPrefix `json:"prefix,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Prefix IPPrefix `json:"prefix,omitempty"`
Prefix IPPrefix `json:"prefix"`

omitempty is unused if the field is required. Also applies to the NextHops.


// +required
// +kubebuilder:validation:MinItems=1
NextHops []*NextHop `json:"nextHops,omitempty"`
}

type NextHop struct {
// TODO(sven-rosenzweig): It is possible to point an a static route in a VRF to an Interface. For now this is not needed.
// InterfaceRef is a reference to the Interface resource that this static route is associated with.
// The referenced Interface must exist in the same namespace.
// +optional
// InterfaceRef *LocalObjectReference `json:"interfaceRef,omitempty"`

// Address is the IP address of the next hop for the static route.
// +required
Address string `json:"address,omitempty"`

// Metric assigns a priority to the static route. Lower values indicate higher priority.
// +optional
Metric *int32 `json:"metric,omitempty"`
}

// StaticRouteStatus defines the observed state of StaticRoute.
type StaticRouteStatus struct {
// The conditions are a list of status objects that describe the state of the Interface.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The conditions are a list of status objects that describe the state of the Interface.
// The conditions are a list of status objects that describe the state of the StaticRoute.

copy+pasta

// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="VRF",type=string,JSONPath=`.spec.vrfRef.name`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// StaticRoute is the Schema for the staticroutes API
type StaticRoute struct {
metav1.TypeMeta `json:",inline"`

// metadata is a standard object metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`

// spec defines the desired state of StaticRoute
// +required
Spec StaticRouteSpec `json:"spec"`

// status defines the observed state of StaticRoute
// +optional
Status StaticRouteStatus `json:"status,omitzero"`
}

// GetConditions implements conditions.Getter.
func (sr *StaticRoute) GetConditions() []metav1.Condition {
return sr.Status.Conditions
}

// SetConditions implements conditions.Setter.
func (sr *StaticRoute) SetConditions(conditions []metav1.Condition) {
sr.Status.Conditions = conditions
}

// +kubebuilder:object:root=true

// StaticRouteList contains a list of StaticRoute
type StaticRouteList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []StaticRoute `json:"items"`
}

var (
StaticRouteDependencies []schema.GroupVersionKind
staticRouteDependenciesMu sync.Mutex
)

func RegisterStaticRouteDependency(gvk schema.GroupVersionKind) {
staticRouteDependenciesMu.Lock()
defer staticRouteDependenciesMu.Unlock()
StaticRouteDependencies = append(StaticRouteDependencies, gvk)
}

func init() {
SchemeBuilder.Register(func(s *runtime.Scheme) error {
s.AddKnownTypes(GroupVersion, &StaticRoute{}, &StaticRouteList{})
return nil
})
}
139 changes: 139 additions & 0 deletions api/core/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 charts/network-operator/templates/rbac/manager-role.yaml

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

14 changes: 14 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,20 @@ func main() { //nolint:gocyclo
setupLog.Error(err, "Failed to create controller", "controller", "pool-ipprefix")
os.Exit(1)
}

if err := (&corecontroller.StaticRouteReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorder("staticroute-controller"),
WatchFilterValue: watchFilterValue,
Provider: prov,
Locker: locker,
RequeueInterval: requeueInterval,
}).SetupWithManager(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "StaticRoute")
os.Exit(1)
}

if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if err := webhookv1alpha1.SetupVRFWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "VRF")
Expand Down
Loading
Loading